Skip to content

Threejs实现星体运动

完整代码

html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        html,
        body {
            overflow: hidden;
        }

        .webgl {
            position: fixed;
            top: 0;
            left: 0;
            outline: none;
            height: 100vh;
            width: 100vw;
        }
    </style>
</head>

<body>
    <canvas class="webgl"></canvas>


    <script type="module">
        import * as THREE from "../libs/three.module.js"
        import { OrbitControls } from 'https://threejsfundamentals.org/threejs/resources/threejs/r132/examples/jsm/controls/OrbitControls.js'


        let renderer = null;

        let camera = null;

        let scene = null;

        let controls = null;

        let canvas = null;

        let loader = null;

        let plantMesh, ringMesh, torusMesh, starGroup, satellite = null;
        
        // 声明一个变量angle表示角度位置      
        let angle = 0;


        // 定义渲染尺寸
        const sizes = {
            width: window.innerWidth,
            height: window.innerHeight
        }

        // 初始化渲染器
        const initRenderer = () => {
            canvas = document.querySelector('canvas.webgl');

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

            renderer.setSize(sizes.width, sizes.height)

            renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        }

        // 初始化场景
        const initScene = () => {
            scene = new THREE.Scene()
            scene.background = new THREE.Color(0x1A1A1A)
            // 场景雾化
            scene.fog = new THREE.Fog(0x1A1A1A, 1, 1000)
        }

        // 初始化相机
        const initCamera = () => {
            camera = new THREE.PerspectiveCamera(40, sizes.width / sizes.height, 0.1, 1000)
            scene.add(camera)
            camera.position.set(20, 100, 450)
        }

        // 初始化控制器
        const initControl = () => {
            controls = new OrbitControls(camera, renderer.domElement)
            // 开启控制器的移动惯性,鼠标交互过程会更加流畅和逼真
            controls.enableDamping = true
        }

        // 初始化灯光
        const initLight = () => {
            const light = new THREE.AmbientLight(0xdeedff, 1.5)
            scene.add(light)
        }

        // 创建星球
        const initMesh = () => {
            // Lambert 材质,考虑光照影响的材质,用于创建暗淡的、不光亮的物体。
            const material = new THREE.MeshLambertMaterial({
                color: 0x03c03c,
                wireframe: true,
            });
            // 创建球体
            const sphereGeometry = new THREE.SphereGeometry(80, 32, 32)
            plantMesh = new THREE.Mesh(sphereGeometry, material)
            scene.add(plantMesh)

            // 创建紫色轨道
            const torusMaterial = new THREE.MeshLambertMaterial({
                color: 0xaa61ed,
                wireframe: true
            })
            const torusGeometry = new THREE.TorusGeometry(150, 8, 2, 120)
            torusMesh = new THREE.Mesh(torusGeometry, torusMaterial)
            torusMesh.rotation.x = Math.PI / 2
            torusMesh.rotation.y = 0.4 * (Math.PI / 2);
            scene.add(torusMesh)


            // 添加蓝色轨道
            const ringMaterial = new THREE.MeshLambertMaterial({
                color: 0x40a9ff,
                wireframe: true
            })
            const ringGeometry = new THREE.RingGeometry(140, 160, 120)
            ringMesh = new THREE.Mesh(ringGeometry, ringMaterial)
            ringMesh.rotation.x = Math.PI / 2
            ringMesh.rotation.y = -0.1 * (Math.PI / 2);
            scene.add(ringMesh)

        }

        // 创建星星
        const initStar = () => {
            // 创建一个网格组
            starGroup = new THREE.Group()
            // 创建1000颗星星
            for (let i = 0; i < 1000; i++) {
                // 创建20面几何体
                const geometry = new THREE.IcosahedronGeometry(Math.random() * 2, 0);
                // 卡通网格材质
                const material = new THREE.MeshToonMaterial({ color: 0xeeeeee });
                // 创建20面几何体模拟星星,随机设置星星的位置与旋转方向
                const mesh = new THREE.Mesh(geometry, material)
                mesh.position.x = (Math.random() - 0.5) * 700;
                mesh.position.y = (Math.random() - 0.5) * 700;
                mesh.position.z = (Math.random() - 0.5) * 700;
                mesh.rotation.x = Math.random() * 2 * Math.PI;
                mesh.rotation.y = Math.random() * 2 * Math.PI;
                mesh.rotation.z = Math.random() * 2 * Math.PI;
                starGroup.add(mesh)
            }
            scene.add(starGroup)
        }

        // 创建卫星
        const initSatellite = () => {
            const IcoGeometry = new THREE.IcosahedronGeometry(16, 0);
            const IcoMaterial = new THREE.MeshToonMaterial({ color: 0xfffc00 });
            satellite = new THREE.Mesh(IcoGeometry, IcoMaterial);
            scene.add(satellite);
        }

        // 场景和相机放入渲染器中
        const render = (time = 1) => {
            const axis = new THREE.Vector3(0, 0, 1);
            time *= 0.001

            // 中心球体旋转
            plantMesh.rotation.y = time

            // 设置轨道动画
            ringMesh.rotateOnAxis(axis, Math.PI / 400);
            torusMesh.rotateOnAxis(axis, Math.PI / 400);

            // 卫星动画
            // 每次执行渲染函数redner时候,角度累加0.005
            angle += 0.005;
            // 圆周运动网格模型x坐标计算  绕转半径200
            satellite.position.x = 250 * Math.sin(angle) 
            // 圆周运动网格模型y坐标计算  绕转半径200
            satellite.position.z = 250 * Math.cos(angle)

            // 星星动画
            starGroup.rotation.y += 0.0009;
            starGroup.rotation.z -= 0.0003;

            // 加载渲染器
            renderer.render(scene, camera)

            // 开始动画
            requestAnimationFrame(render)
        }

        // 页面缩放事件监听
        window.addEventListener('resize', () => {
            sizes.width = window.innerWidth;
            sizes.height = window.innerHeight;
            // 更新渲染
            renderer.setSize(sizes.width, sizes.height);
            renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
            // 更新相机
            camera.aspect = sizes.width / sizes.height;
            camera.updateProjectionMatrix();
        });

        initScene()
        initRenderer()
        initCamera()
        initControl()
        initLight()
        initMesh()
        initStar()
        initSatellite()

        requestAnimationFrame(render)


    </script>
</body>

</html>

项目难点分析

星球和轨道

js
        // 创建星球
        const initMesh = () => {
            // Lambert 材质,考虑光照影响的材质,用于创建暗淡的、不光亮的物体。
            const material = new THREE.MeshLambertMaterial({
                color: 0x03c03c,
                wireframe: true,
            });
            // 创建球体
            const sphereGeometry = new THREE.SphereGeometry(80, 32, 32)
            plantMesh = new THREE.Mesh(sphereGeometry, material)
            scene.add(plantMesh)

            // 创建紫色轨道
            const torusMaterial = new THREE.MeshLambertMaterial({
                color: 0xaa61ed,
                wireframe: true
            })
            const torusGeometry = new THREE.TorusGeometry(150, 8, 2, 120)
            torusMesh = new THREE.Mesh(torusGeometry, torusMaterial)
            torusMesh.rotation.x = Math.PI / 2
            torusMesh.rotation.y = 0.4 * (Math.PI / 2);
            scene.add(torusMesh)


            // 添加蓝色轨道
            const ringMaterial = new THREE.MeshLambertMaterial({
                color: 0x40a9ff,
                wireframe: true
            })
            const ringGeometry = new THREE.RingGeometry(140, 160, 120)
            ringMesh = new THREE.Mesh(ringGeometry, ringMaterial)
            ringMesh.rotation.x = Math.PI / 2
            ringMesh.rotation.y = -0.1 * (Math.PI / 2);
            scene.add(ringMesh)

        }

        // 场景和相机放入渲染器中
        const render = (time = 1) => {
            const axis = new THREE.Vector3(0, 0, 1);
            time *= 0.001

            // 中心球体旋转
            plantMesh.rotation.y = time

            // 设置轨道动画
            ringMesh.rotateOnAxis(axis, Math.PI / 400);
            torusMesh.rotateOnAxis(axis, Math.PI / 400);

            // 加载渲染器
            renderer.render(scene, camera)

            // 开始动画
            requestAnimationFrame(render)
        }

星星

星星需要不断的运动,并且形状、位置都不一样。

js
        const starGroup = null
        // 创建星星
        const initStar = () => {
            // 创建一个网格组
            starGroup = new THREE.Group()
            // 创建1000颗星星
            for (let i = 0; i < 1000; i++) {
                // 创建20面几何体
                const geometry = new THREE.IcosahedronGeometry(Math.random() * 2, 0);
                // 卡通网格材质
                const material = new THREE.MeshToonMaterial({ color: 0xeeeeee });
                // 创建20面几何体模拟星星,随机设置星星的位置与旋转方向
                const mesh = new THREE.Mesh(geometry, material)
                mesh.position.x = (Math.random() - 0.5) * 700;
                mesh.position.y = (Math.random() - 0.5) * 700;
                mesh.position.z = (Math.random() - 0.5) * 700;
                mesh.rotation.x = Math.random() * 2 * Math.PI;
                mesh.rotation.y = Math.random() * 2 * Math.PI;
                mesh.rotation.z = Math.random() * 2 * Math.PI;
                starGroup.add(mesh)
            }
            scene.add(starGroup)
        }

        // 场景和相机放入渲染器中
        const render = (time = 1) => {
            time *= 0.001

            // 星星动画
            starGroup.rotation.y += 0.0009;
            starGroup.rotation.z -= 0.0003;

            // 加载渲染器
            renderer.render(scene, camera)

            // 开始动画
            requestAnimationFrame(render)
        }

卫星运动

js
        var angle = 0
        // 创建卫星
        const initSatellite = () => {
            const IcoGeometry = new THREE.IcosahedronGeometry(16, 0);
            const IcoMaterial = new THREE.MeshToonMaterial({ color: 0xfffc00 });
            satellite = new THREE.Mesh(IcoGeometry, IcoMaterial);
            scene.add(satellite);
        }

        // 场景和相机放入渲染器中
        const render = (time = 1) => {
            const axis = new THREE.Vector3(0, 0, 1);
            time *= 0.001

            // 卫星动画
            // 每次执行渲染函数redner时候,角度累加0.005
            angle += 0.005;
            // 圆周运动网格模型x坐标计算  绕转半径200
            satellite.position.x = 250 * Math.sin(angle) 
            // 圆周运动网格模型y坐标计算  绕转半径200
            satellite.position.z = 250 * Math.cos(angle)

            // 加载渲染器
            renderer.render(scene, camera)

            // 开始动画
            requestAnimationFrame(render)
        }

效果展示

上次更新于: