HTML&CSS:用Three.js创造3D书本世界

这个页面实现了一个带有3D动画效果的网页,用户可以看到一个3D场景中的书本随机放置并旋转。页面使用了Three.js库和GSAP库来实现3D效果和动画,整体设计风格现代且具有视觉吸引力。


大家复制代码时,可能会因格式转换出现错乱,导致样式失效。建议先少量复制代码进行测试,若未能解决问题,私信回复源码两字,我会发送完整的压缩包给你。

演示效果

HTML&CSS

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.2/TweenMax.min.js"></script>
    <script src="https://unpkg.com/[email protected]/examples/js/controls/OrbitControls.js"></script>
    <title>公众号关注:前端Hardy</title>
    <style>
        html,
        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
            background-color: #000;
            font-family: monospace;
            color: white;
        }

        body {

            font-family: Bague, sans-serif;
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale;
            background: rgb(48, 68, 67);

            background: #628f84;
            background: #768ca8;
            background: #d7dddd;
            background: #e4eeef;


        }

        body:after {
            content: "";
            position: fixed;
            top: -10rem;
            left: -10rem;
            width: calc(100vw + 100rem);
            height: calc(100vh + 100rem);
            background-image: image-set(url("https://i.imgur.com/N0STPcn.png") type("image/png"));
            opacity: 0.4;
            z-index: 1000;
            animation: noise 1s steps(2) infinite;
        }

        @keyframes noise {
            0% {
                transform: translate3d(0, 9rem, 0);
            }

            50% {
                transform: translate3d(-9rem, -4rem, 0);
            }

            100% {
                transform: translate3d(-7rem, 0, 0);
            }
        }

        .header {
            position: fixed;
            top: 0;
            width: 100%;
            padding: 1rem;
            text-align: center;
            background: rgba(0, 0, 0, 0.5);
            z-index: 10;
        }

        #canvas-container {
            height: 200vh;
            width: 100%;
            position: relative;
        }

        canvas {
            position: absolute;
            top: 0;
            left: 0;
            height: 100vh;
            width: 100vw;
            display: block;
            z-index: 1;
        }

        .overlay-text {
            position: absolute;
            top: 50%;
            left: 15%;
            transform: translateY(-50%);
            text-align: left;
            z-index: 2;
            font-size: 35px;
            font-weight: 400;
            mix-blend-mode: difference;
            color: grey;
            max-width: 50%;
            line-height: 1.3;
            pointer-events: none;
        }


        .color-change {
            color: inherit;
        }
    </style>

<body>
    <div id="canvas-container"></div>

    <div class="overlay-text">
        <div>
            <span class="color-change">
                I write fiction to take readers on a journey that explores the human condition,both good and bad. 
            </span>
        </div>
    </div>
    <script>
        window.addEventListener('load', init, false);

        function init() {
            createWorld();
            createLights();
            createPrimitive();
            animation();
        }

        var Theme = {
            primary: 0xd7dddd,
            secundary: 0x0000FF,
            danger: 0xFF0000,
            darker: 0x101010
        };

        var scene, camera, renderer, controls;
        var _group = new THREE.Group();

        function createWorld() {
            const _width = window.innerWidth;
            const _height = document.getElementById('canvas-container').clientHeight;

            scene = new THREE.Scene();
            scene.fog = new THREE.Fog(Theme.primary, 9, 13);
            scene.background = null;


            camera = new THREE.PerspectiveCamera(35, _width / _height, 1, 1000);
            camera.position.set(0, 0, 10);

            renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });

            renderer.setSize(_width, _height);
            renderer.shadowMap.enabled = true;
            renderer.shadowMap.type = THREE.PCFSoftShadowMap;

            controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.enableZoom = false;
            controls.update();

            document.getElementById('canvas-container').appendChild(renderer.domElement);

            window.addEventListener('resize', onWindowResize, false);
        }

        function onWindowResize() {
            const _width = window.innerWidth;
            const _height = window.innerHeight;
            renderer.setSize(_width, _height);
            camera.aspect = _width / _height;
            camera.updateProjectionMatrix();
        }

        function createLights() {
            const hemiLight = new THREE.HemisphereLight(Theme.primary, Theme.darker, 1);
            const dirLight = new THREE.DirectionalLight(0xFFFFFF, 1);
            dirLight.position.set(10, 20, 20);
            dirLight.castShadow = true;
            dirLight.shadow.mapSize.width = 5000;
            dirLight.shadow.mapSize.height = 5000;
            dirLight.penumbra = 0.8;

            scene.add(hemiLight);
            scene.add(dirLight);
        }

        function CreateBook() {
            this.mesh = new THREE.Object3D();

            const geo_cover = new THREE.BoxGeometry(2.4, 3, 0.05);
            const lmo_cover = new THREE.BoxGeometry(0.05, 3, 0.59);
            const ppr_cover = new THREE.BoxGeometry(2.3, 2.8, 0.5);

            const dartmouthGreens = [0x475b47];
            const randomGreen = dartmouthGreens[Math.floor(Math.random() * dartmouthGreens.length)];

            const mat = new THREE.MeshPhongMaterial({ color: randomGreen });
            const mat_paper = new THREE.MeshPhongMaterial({ color: 0xFFFFFF });

            const _cover1 = new THREE.Mesh(geo_cover, mat);
            const _cover2 = new THREE.Mesh(geo_cover, mat);
            const _lomo = new THREE.Mesh(lmo_cover, mat);
            const _paper = new THREE.Mesh(ppr_cover, mat_paper);

            [_cover1, _cover2, _lomo, _paper].forEach(mesh => {
                mesh.castShadow = true;
                mesh.receiveShadow = true;
            });

            _cover1.position.z = 0.3;
            _cover2.position.z = -0.3;
            _lomo.position.x = 2.4 / 2;

            this.mesh.add(_cover1, _cover2, _lomo, _paper);
        }

        function isTooClose(newObj, others, minDistance = 1.5) {
            const newPos = newObj.position;
            for (let existing of others) {
                const dx = newPos.x - existing.position.x;
                const dy = newPos.y - existing.position.y;
                const dz = newPos.z - existing.position.z;
                const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
                if (dist < minDistance) return true;
            }
            return false;
        }

        function createPrimitive() {
            const placedBooks = [];
            const a = 2;

            for (let i = 0; i < 12; i++) {
                const _object = new CreateBook();
                const s = 0.1 + Math.random() * 0.4;
                _object.mesh.scale.set(s, s, s);

                let tries = 0;
                do {
                    _object.mesh.position.x = (Math.random() - 0.5) * a * 2;
                    _object.mesh.position.y = (Math.random() - 0.5) * a * 2;
                    _object.mesh.position.z = (Math.random() - 0.5) * a * 2;
                    tries++;
                } while (isTooClose(_object.mesh, placedBooks) && tries < 20);

                _object.mesh.rotation.x = Math.random() * 2 * Math.PI;
                _object.mesh.rotation.y = Math.random() * 2 * Math.PI;
                _object.mesh.rotation.z = Math.random() * 2 * Math.PI;

                TweenMax.to(_object.mesh.rotation, 8 + Math.random() * 8, {
                    x: (Math.random() - 0.5) * 0.5,
                    y: (Math.random() - 0.5) * 0.5,
                    z: (Math.random() - 0.5) * 0.5,
                    yoyo: true,
                    repeat: -1,
                    ease: Sine.easeInOut,
                    delay: 0.05 * i
                });

                _group.add(_object.mesh);
                placedBooks.push(_object.mesh);
            }

            scene.add(_group);
            _group.position.x = 2;
        }

        function animation() {
            _group.rotation.x -= 0.003;
            _group.rotation.y -= 0.003;
            _group.rotation.z -= 0.003;
            controls.update();
            requestAnimationFrame(animation);
            renderer.render(scene, camera);
        }
    </script>
</body>

</html>

HTML 结构

  • canvas-container:用于放置Three.js渲染的3D内容。
  • overlay-text:包含覆盖在3D内容上的文本。
  • script:包含页面的JavaScript代码,用于初始化和控制3D场景。

CSS 样式

  • html, body:设置外边距和内边距为0。禁止内容溢出,确保全屏显示。设置背景颜色为黑色。设置字体为monospace,颜色为白色。

  • body:after:添加一个固定背景图,使用image-set设置图片。设置透明度为0.4。使用@keyframes定义动画效果,使背景图在不同位置之间移动。

  • .header:固定在页面顶部,宽度为100%。设置内边距、文本居中、背景颜色和z-index。

  • #canvas-container:设置高度为200vh,宽度为100%。设置为相对定位。

  • canvas:设置为绝对定位,覆盖整个视口。设置为块级元素,确保全屏显示。设置z-index为1。

  • .overlay-text:设置为绝对定位,垂直居中。设置文本对齐方式、字体大小、权重、混合模式和颜色。设置最大宽度和行高。设置pointer-events为none,避免鼠标事件干扰。

JavaScript功能说明

初始化

  • init():在页面加载完成后调用,初始化3D场景、灯光、物体和动画。
  • createWorld():创建3D场景、相机、渲染器和轨道控制。
  • createLights():创建环境光和方向光。
  • createPrimitive():创建多个3D物体(书本),并随机放置在场景中。
  • animation():定义动画循环,更新场景的旋转和渲染。

3D场景

  • 使用Three.js库创建3D场景。
  • 使用THREE.PerspectiveCamera创建透视相机。
  • 使用THREE.WebGLRenderer渲染场景。
  • 使用THREE.OrbitControls控制相机的旋转和缩放。

灯光

  • 使用THREE.HemisphereLight创建环境光。
  • 使用THREE.DirectionalLight创建方向光,并启用阴影。

物体

  • 使用THREE.BoxGeometry创建书本的几何形状。
  • 使用THREE.MeshPhongMaterial创建材质。
  • 使用TweenMax库为书本添加动画效果。

动画

  • 使用requestAnimationFrame实现持续的动画循环。
  • 更新场景的旋转和相机控制。
  • 渲染场景。

各位互联网搭子,要是这篇文章成功引起了你的注意,别犹豫,关注、点赞、评论、分享走一波,让我们把这份默契延续下去,一起在知识的海洋里乘风破浪!

相关推荐
itsOli35 分钟前
(20)详情页开发——② 公用图片画廊组件 | Vue.js 项目实战: 移动端“旅游网站”开发
前端·javascript·vue.js
Riesenzahn37 分钟前
说说你对JSBridge的理解
前端·javascript
烛阴41 分钟前
Express 中间件:Node.js 开发的得力助手
前端·javascript·express
百锦再1 小时前
Reactive编程框架与工具
前端·javascript·python·django·vue·框架·react
江城开朗的豌豆1 小时前
CSS篇:移动端适配必学:750设计图的px到rem精准换算方法
前端·css·面试
ChinaRainbowSea1 小时前
9. RabbitMQ 消息队列幂等性,优先级队列,惰性队列的详细说明
java·javascript·分布式·后端·rabbitmq·ruby·java-rabbitmq
就是我1 小时前
JavaScript这几种内存泄露写法,你要小心了
前端·javascript·代码规范
日升1 小时前
时区转换工具+PWA离线网页
前端·javascript
那小孩儿1 小时前
还在每次都写判断?试试惰性函数,让你的代码更聪明!
前端·javascript
用户72526496629691 小时前
Cesium:加载本地高程/地形数据
javascript