模仿 SU7 网页特效 实现3D旋转地球

2,741 阅读4分钟

随着小米 SU7 的发布,不少网友发现了一个小米su7的展示网页,那是相当的酷炫

MMMMM.gif

当你体验一遍后,会看到各种炫酷的效果

包括隧道穿梭、波浪动画等,并且还有些细节也值得注意,如地面的反射效果。

屏幕截图 2024-12-24 230551.png

原网页:gamemcu.com/su7/

那么这个网页是用哪个技术实现的呢?答案是three.js,不论是从控制台,还是一些技术解析插件都能得知。

现在,前端开发不再局限于2D图形和静态页面。随着WebGL和JavaScript库如Three.js的发展,创建复杂且互动性高的3D内容变得更加简单。今天,我们将一起探索如何使用Three.js创建一个交互式的3D地球,并了解这个过程中涉及到的关键概念和技术。

要创建3D地球就要用下面到2D的地球图片

land_ocean_ice_cloud_2048.jpg

准备工作环境

首先,我们需要确保我们的HTML文档已经正确设置了<canvas>标签,它将作为Three.js渲染3D内容的目标。同时,我们引入了Three.js库,这是实现3D效果的核心:

html
深色版本
<script src="https://unpkg.com/three@0.128.0/build/three.min.js"></script>
<canvas id="webglcanvas"></canvas>

创建场景、相机和渲染器

接下来,我们要初始化场景(Scene)、相机(Camera)以及渲染器(Renderer)。这些元素共同构成了3D空间的基础框架,允许我们在网页上展示3D物体。

javascript
深色版本
function init() {
  canvas = document.getElementById('webglcanvas'); // 获取Canvas DOM元素
  camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 2000);
  camera.position.z = 500; // 设置相机位置
  
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0xffffff); // 设置背景颜色为白色
  
  renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: true});
  renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染尺寸
}

init函数:初始化 3D 场景

function init() {
    canvas = document.getElementById('webglcanvas'); // DOM 
    camera = new THREE.PerspectiveCamera(60,  
    window.innerWidth / window.innerHeight, 1, 2000); // 实例化 相机
    // 相机离scene场景
    camera.position.z = 500;
    scene = new THREE.Scene(); // 实例化 场景
    scene.background = new THREE.Color(0xffffff); // 背景色
    group = new THREE.Group();// 组
    scene.add(group);
    // 纹理加载器
    let loader = new THREE.TextureLoader(); // 简单的加载器
    loader.load('land_ocean_ice_cloud_2048.jpg', function(texture) {
      let geometry = new THREE.SphereGeometry(200, 20, 20); // 球体
      let material = new THREE.MeshBasicMaterial({ // 材质
        map: texture
      });
      let mesh = new THREE.Mesh(geometry, material); // 网格
      group.add( mesh );
      // 渲染器 目标是canvas 
      renderer = new THREE.WebGLRenderer({
        canvas: canvas,
        antialias: true
      });
      renderer.setSize(window.innerWidth, window.innerHeight);
      // renderer.render(scene, camera);
      document.addEventListener('mousemove', onDocumentMouseMove, false);
    }) 
}
  • 首先,通过document.getElementById获取到页面中的元素并赋值给canvas变量,建立起 JavaScript 与 HTML 页面元素的连接,后续操作才有了具体的 “画板”。
  • 接着创建PerspectiveCamera实例,参数60是视野角度,类似人眼视角范围,window.innerWidth / window.innerHeight是相机的宽高比,保证画面不变形,1和2000分别是近裁剪面和远裁剪面距离,划定可见范围。将camera.position.z = 500,把相机拉远到合适距离,能完整呈现 3D 地球全貌。
  • 创建Scene实例,设置白色背景,为后续添加的 3D 对象提供干净整洁的展示环境。
  • 实例化Group并添加到scene,为组织 3D 元素做准备。
  • 关键的纹理加载环节,使用TextureLoader加载地球纹理图片,当图片加载成功回调函数内:
    • 用SphereGeometry构建球体几何形状,半径200,20和20分别是经度和纬度方向的分段数,分段越多球体越圆润。
    • 结合加载的纹理创建MeshBasicMaterial材质,将纹理映射到球体表面。
    • 最后通过Mesh把几何形状与材质组合成 3D 地球模型,并添加到group中。
    • 同时创建WebGLRenderer实例,关联canvas,开启抗锯齿让画面更平滑,设置渲染尺寸与窗口一致,并添加鼠标移动事件监听器,为交互埋下伏笔。

onDocumentMouseMove函数:捕捉鼠标移动

function onDocumentMouseMove(event) {
    mouseX = event.clientX - windowHalfX;
    mouseY = event.clientY - windowHalfY;
}

这个函数简洁明了,每次鼠标在页面移动时,它获取鼠标当前位置clientX和clientY,减去窗口中心坐标windowHalfX和windowHalfY,得到鼠标相对窗口中心的偏移量,实时更新mouseX和mouseY变量,后续用于控制相机移动实现交互。

animate和render函数:让 3D 地球动起来

function animate() {
    // 递归 屏幕的刷帧率  60帧/s
    requestAnimationFrame(animate)
    render()
}
function render() {
    camera.position.x += (mouseX - camera.position.x) * 0.05;
    camera.position.y += (mouseY - camera.position.y) * 0.05;
    camera.lookAt(scene.position);
    // camera.lookAt(scene.position)
    group.rotation.y -= 0.005;
    renderer.render(scene, camera);
}

animate函数利用requestAnimationFrame以每秒 60 帧的频率递归调用自身,形成流畅的动画循环。每次循环调用render函数:

  • 在render函数里,根据鼠标偏移量,以一定系数0.05缓慢调整相机位置,让相机跟随鼠标移动,仿佛我们转动头部观察地球;
  • 同时让group(包含地球模型)绕 y 轴以-0.005的速度缓慢旋转,模拟地球自转;
  • 最后通过renderer.render将实时更新的 3D 场景绘制到canvas上,呈现出动态逼真的 3D 地球效果。

    最终效果如下

video.gif

总之,WebGL和Three.js 的结合为前端开发注入了强大动力,赋予了我们创造神奇 3D 世界的能力,开启了无限可能的创意空间。无论是构建沉浸式的游戏场景、逼真的数字孪生模型,还是打造令人惊叹的可视化数据展示,它们都能胜任。希望大家都能深入探索。

屏幕截图 2024-12-01 221630.png

看到这就点个赞吧