海上生明月,天涯共此时。又是一年中秋时,回想上一次赏月已是那遥远的童年时光,忙碌使我们忘却了假日应有的舒缓。今天在这假期即将开始的时候,让我们用代码来过个节。
今天的主题是基于threejs画出月球环绕地球运动的效果,并增加飞跃星空的感觉,如封面图所示。
球体绘制
首先绘制出地球和月球,基于SphereBufferGeometry类绘制三维球体,参数如下:
参数 | 描述 |
---|---|
radius | 该属性定义球体的半径,默认值是50 |
widthSegments | 该属性指定竖直方向上的分段数,段数越多,球体的表面越光滑,默认值是8,最小值是3 |
heightSegments | 该属性指定水平方向上的分段数,段数越多,球体的表面越光滑,默认值是6,最小值是2 |
phiStart | 该属性用来指定从x轴的什么位置开始绘制,取值范围是0到2*π,默认值0 |
phiLength | 该属性用来指定从phiStart开始画多少,默认2*π(画整球) |
thetaStart | 该属性用来指定从y轴的什么位置开始绘制,取值范围是0到2*π,默认值0 |
thetaLength | 该属性用来指定从thetaStart开始画多少,默认2*π(画整球) |
首先在网上找一张地球平面图,此处只需要用到前面三个参数即可new THREE.SphereBufferGeometry(10, 50, 50)
,效果图如下:
月球图类似,只是会相对地球绘制的球体会小一些。
球体动画
然后是给两个球体增加动画的效果,地球本身的自转效果,月球围绕地球环绕运动的效果。
地球自转效果比较简单,只需要不断修改地球实例的y轴的值即可。
planet.rotation.y += 0.002;
月球比较复杂,除开自转的同时还需增加一个环线运动效果,这里增加一个t值,默认为0,在动画运动函数中不断增加t值,并结合数学三角函数实现环绕效果。
moon.rotation.y -= 0.007;
moon.position.x = 15 * Math.cos(t) + 0;
moon.position.z = 20 * Math.sin(t) - 35;
t += 0.015;
最终动画的效果如下所示:
星空轨迹
只有两个球体运动相对单调,所以再增加星空运动的轨迹提示氛围感。由于星星是不断运动飞出屏幕之外的,所以我们要有重新绘制的机制。
绘制星星
let lineTotal = 1000;
let linesGeometry = new THREE.BufferGeometry();
linesGeometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(6 * lineTotal), 3));
linesGeometry.setAttribute("velocity", new THREE.BufferAttribute(new Float32Array(2 * lineTotal), 1));
let l_positionAttr = linesGeometry.getAttribute("position");
let l_vertex_Array = linesGeometry.getAttribute("position").array;
let l_velocity_Array = linesGeometry.getAttribute("velocity").array;
for (let i = 0; i < lineTotal; i++) {
let x = THREE.MathUtils.randInt(-100, 100);
let y = THREE.MathUtils.randInt(10, 100);
if (x < 7 && x > -7 && y < 20) x += 14;
let z = THREE.MathUtils.randInt(0, -300);
l_vertex_Array[6 * i + 0] = l_vertex_Array[6 * i + 3] = x;
l_vertex_Array[6 * i + 1] = l_vertex_Array[6 * i + 4] = y;
l_vertex_Array[6 * i + 2] = l_vertex_Array[6 * i + 5] = z;
l_velocity_Array[2 * i] = l_velocity_Array[2 * i + 1] = 0;
}
let starsMaterial = new THREE.LineBasicMaterial({
color: "#ffffff",
transparent: true,
opacity: 0.5,
fog: false
});
let lines = new THREE.LineSegments(linesGeometry, starsMaterial);
linesGeometry.getAttribute("position").setUsage(THREE.DynamicDrawUsage);
scene.add(lines);
增加动画及重置
for (let i = 0; i < lineTotal; i++) {
l_velocity_Array[2 * i] += 0.0049;
l_velocity_Array[2 * i + 1] += 0.005;
l_vertex_Array[6 * i + 2] += l_velocity_Array[2 * i];
l_vertex_Array[6 * i + 5] += l_velocity_Array[2 * i + 1];
if (l_vertex_Array[6 * i + 2] > 50) {
l_vertex_Array[6 * i + 2] = l_vertex_Array[6 * i + 5] = THREE.MathUtils.randInt(-200, 10);
l_velocity_Array[2 * i] = 0;
l_velocity_Array[2 * i + 1] = 0;
}
}
星空背景动画
正常的星空背景都会有一些星云,在此基础上再增加一张背景图的运动使之更加真实。星空背景本质也是一个球体,只是半径相对较大,肉眼看不出来。
const textureSphereBg = loader.load('https://xxx.jpg');
textureSphereBg.anisotropy = 16;
const geometrySphereBg = new THREE.SphereBufferGeometry(50, 32, 32);
const materialSphereBg = new THREE.MeshBasicMaterial({
side: THREE.BackSide,
map: textureSphereBg,
fog: false
});
sphereBg = new THREE.Mesh(geometrySphereBg, materialSphereBg);
sphereBg.position.set(0, 50, 0);
scene.add(sphereBg);
然后在整体的动画函数中修改不同方向的数值,使之感觉游荡在太空中的感觉。
sphereBg.rotation.x += 0.002;
sphereBg.rotation.y += 0.002;
sphereBg.rotation.z += 0.002;
最后
整体实现功能就结束了,里面用到大量的threejs的相关API,有兴趣的同学可以去研究研究,最后的完整代码如下:
https://code.juejin.cn/pen/7141241609026273321
参考
https://codepen.io/isladjan/pen/zYqLxeG
看完如果觉得有趣,记得点赞收藏起来。说不定哪天就用上啦~
最后祝大家中秋快乐,假期玩得开心~
专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)
暂无评论