• JS实现环绕地球飞行的3D飞行线动画效果(JS+HTML)


    1.项目介绍

    JS+HTML实现绕地球飞行的3D飞行线动画效果,且3D地球可以随意拖动和滑动缩放,画面中心是蓝色地球,地球表面上的两点连线之间有光电随机出现沿着抛物线轨迹3D飞行,可使用较好的浏览器打开,如microsoft edge打开,效果如下图所示:

    2.html源码如下所示

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Threejs 实现环绕地球飞行的3D飞行线</title>
    <style>
            html,
            body {
                width: 100%;
                height: 100%;
                margin: 0;
                overflow: hidden;
            }
    </style>
    </head>
    <body>
    <div id="container"></div>
    <script src="js/three.103.min.js"></script>
    <script src="js/OrbitControls.js"></script>
    
    <script id="vertex-shader" type="x-shader/x-vertex">
        varying vec3 vPosition;
        varying vec4 vColor;
        void main() {
            vPosition = position;
            vColor = vec4(color, 1.0);
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
    </script>
    
    <script id="fragment-shader" type="x-shader/x-fragment">
        uniform vec3 targetPos; // 目标位置
        uniform float vLength;  // 距离
    
        uniform float time;
        varying vec3 vPosition;
        varying vec4 vColor;
        void main() {
            float dist = distance(vPosition, targetPos);
    
            vec4 color = vec4(vColor);
            float p = dist/vLength * 6.0 + time * 1.0;
    
    
            if (p < 3.1415926/2.0){
                p = 0.0;
            }
            if (p > 3.1415926*2.0){
                p = 0.0;
            }
    
            float a = sin(p);
            color.a = a;
    
            gl_FragColor = color;
        }
    </script>
    <script>
    var container = document.getElementById('container');
    var scene, camera, renderer;
    var globeMesh = undefined;
    var groupDots, groupLines, groupAnimDots;
    var animateDots = []; // 小球运动点轨迹
    var controls;
    
    // 配置参数
    var params = {
        pointsLength: 20, // 点数
        globeRadius: 100 // 地球半径
    }
    var vertexShader = document.getElementById('vertex-shader').innerHTML;
    var fragmentShader = document.getElementById('fragment-shader').innerHTML;
    
    const PI2 = Math.PI * 2; // 弧度的取值为0-2π
    var timer = -PI2;
    
    // 预制件
    var Prefab = {
        Sphere: (function() {
            var instance;
            return function(clone = true) {
                if (!instance) {
                    instance = new createSphere();
                }
                if (clone) return instance.clone();
                else return instance;
            }
        })()
    }
    
    init();
    update();
    
    function init() {
        // 场景
        scene = new THREE.Scene();
        groupDots = new THREE.Group();
        groupLines = new THREE.Group();
        groupAnimDots = new THREE.Group();
        scene.add(groupDots, groupLines, groupAnimDots);
    
        // 相机
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
        camera.position.x = -200;
        camera.position.y = 200;
        camera.position.z = -200;
        camera.lookAt(scene.position);
    
        // 渲染器
        renderer = new THREE.WebGLRenderer({
            antialias: true
        });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(window.devicePixelRatio);
        container.appendChild(renderer.domElement);
    
        //
        var light = new THREE.HemisphereLight(0xffffff, 0xffffff, 1);
        scene.add(light);
    
        // 控制器
        controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.minDistance = 200;
        controls.maxDistance = 400;
        controls.rotateSpeed = 0.5;
        controls.enableDamping = true;
        controls.enablePan = false;
    
        initGlobe();
        initLines();
    
        window.addEventListener('resize', onWindowResize, false);
    }
    
    function update() {
        requestAnimationFrame(update);
        renderer.render(scene, camera);
    
        timer += 0.02;
        if (timer > Math.PI * 1.5) {
            timer = -Math.PI * 1.5;
        }
    
        groupLines.children.forEach(function(item) {
            item.material.uniforms.time.value = timer;
        });
    
        // console.log(globeMesh.material.map.offset.x)
    
        controls.update();
    }
    
    function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
    
        renderer.setSize(window.innerWidth, window.innerHeight);
    }
    
    function initGlobe() {
        // 地球
        var geo = new THREE.SphereGeometry(params.globeRadius, 32, 32);
        var texture = new THREE.TextureLoader().load('img/earth.jpg');
        texture.minFilter = THREE.LinearFilter;
        var material = new THREE.MeshPhongMaterial({
            map: texture,
            // wireframe: true
        });
    
        globeMesh = new THREE.Mesh(geo, material);
        scene.add(globeMesh);
    }
    
    // 地球飞线和点
    function initLines() {
        // 球面随机点
        for (let i = 0; i < params.pointsLength; i++) {
            addPoints(groupDots, params.globeRadius);
        }
    
        // 曲线
        groupDots.children.forEach(function(elem, index) {
            // 从第一个点到其它点
            if (elem != groupDots.children[0]) {
                var line = addLines(groupDots.children[index - 1].position, elem.position);
                animateDots.push(line.curve.getPoints(100));
            }
        });
    }
    
    // 3d球面取点
    function getEarthPos(radius, a, b) {
        var x = radius * Math.sin(a) * Math.cos(b);
        var y = radius * Math.sin(a) * Math.sin(b);
        var z = radius * Math.cos(a);
        return {
            x, y, z
        };
    }
    
    // 添加随机点
    function addPoints(group, radius) {
        var mesh = new Prefab.Sphere();
        var pos = getEarthPos(radius, PI2 * Math.random(), PI2 * Math.random());
        mesh.position.set(pos.x, pos.y, pos.z);
        group.add(mesh);
    }
    
    function addLines(v0, v3) {
        var angle = v0.angleTo(v3);
    
        var vtop = v0.clone().add(v3);
        vtop = vtop.normalize().multiplyScalar(params.globeRadius);
    
        var n;
    
        if (angle <= 1) {
            n = params.globeRadius / 5 * angle;
        } else if (angle > 1 && angle < 2) {
            n = params.globeRadius / 5 * Math.pow(angle, 2);
        } else {
            n = params.globeRadius / 5 * Math.pow(angle, 1.5);
        }
    
        var v1 = v0.clone().add(vtop).normalize().multiplyScalar(params.globeRadius + n);
        var v2 = v3.clone().add(vtop).normalize().multiplyScalar(params.globeRadius + n);
    
        // addLineHelper(globeMesh.position, v1);
        // addLineHelper(globeMesh.position, v2);
        // addLineHelper(globeMesh.position, vtop)
    
        // 三维三次贝塞尔曲线(v0起点,v1第一个控制点,v2第二个控制点,v3终点)
        var curve = new THREE.CubicBezierCurve3(v0, v1, v2, v3);
        var points = curve.getPoints(50);
        var vertices = [],
            colors = [];
    
        points.forEach(function(item, index) {
            vertices.push(item.x, item.y, item.z);
            colors.push(index / points.length, index / points.length, index / points.length);
        });
        var geometry = new THREE.BufferGeometry();
    
        geometry.attributes.position = new THREE.Float32BufferAttribute(vertices, 3)
        var material = createLineMaterial(v0, v3);
        var lineMesh = new THREE.Line(geometry, material);
        groupLines.add(lineMesh);
    
        return {
            curve: curve,
            lineMesh: lineMesh
        };
    }
    
    function createLineMaterial(myPos, targetPos) {
        var uniforms = {
            targetPos: {
                value: targetPos
            },
            vLength: {
                value: myPos.distanceTo(targetPos)
            },
            time: {
                value: timer
            }
        };
    
        var material = new THREE.ShaderMaterial({
            uniforms: uniforms,
            vertexShader: vertexShader,
            fragmentShader: fragmentShader,
            transparent: true,
            vertexColors: THREE.VertexColors
        });
    
        return material;
    }
    
    function createSphere() {
        var geometry = new THREE.SphereBufferGeometry(1);
        var material = new THREE.MeshBasicMaterial({
            color: 0x00ffff
        });
        var mesh = new THREE.Mesh(geometry, material);
        return mesh;
    }
    
    function addPointHelper(pos) {
        var mesh = new Prefab.Sphere();
        mesh.material = new THREE.MeshBasicMaterial({
            color: 0xff0000
        });
        mesh.position.copy(pos);
        scene.add(mesh);
    }
    
    function addLineHelper(pos1, pos2) {
        var material = new THREE.LineBasicMaterial({
            color: 0x0000ff
        });
    
        var geometry = new THREE.Geometry();
        geometry.vertices.push(pos1, pos2);
        var line = new THREE.Line(geometry, material);
        scene.add(line);
    }
    </script>
    </body>
    </html>

    3.JS部分代码由于代码量较大这里就不黏贴了

    4.文件目录如下

    5.浏览器要支持HTML5与CSS3才有效果。

     整个项目源码下载地址:https://download.csdn.net/download/xipengbozai/16595841

     

    需要程序源码的可以加我微信x241602私聊。
  • 相关阅读:
    构建一个应用,并在docker compose里运行起来
    docker engine docker-compose部署
    django 返回数据的几种常用姿势
    fiddler+httprunner 零编码实现接口自动化DEMO
    选择排序
    曾经学的那些表示时间复杂度的公式怎么来的?
    python+Airtest+android使用AirtestIDE编写一个DEMO
    怎么计算时间复杂度?
    算法_冒泡排序python+java实现
    2020年1月16日(多进程)
  • 原文地址:https://www.cnblogs.com/huipengbo/p/14638932.html
Copyright © 2020-2023  润新知