持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情
示例代码采用three.js-r73版本: github.com/mrdoob/thre…
我们现在完成了多个粒子系统的静态加载,下面我们要开始完成粒子的运动了。
动态效果展示
先让我们来看看最终展示效果:
很酷炫吧,让我们来完成它。我们先看下我们需要做哪些事情。
- 计算每一帧的时间,让分组进行旋转
- 根据动态或者静态模型,调整每个模型顶点的位置
- 如果是静态模型,不进行运动
- 最开始的时候没有移动,设置移动,向下
- 然后判断粒子是向下还是向上移动,并做动态点位处理
- 如果是向下移动完成,那么休息一会,再向上移动
- 如果是向上移动完成,那么休息一会,再向下移动
计算每一帧时间
- 创建
Clock实例,该实例可用于追踪时间
var clock = new THREE.Clock()
function render() {
// 计算每一帧时间
delta = clock.getDelta()
// 如果每一帧度过的时间大于2微秒,就设置为2,保证每一帧时间都在2微秒以内
delta = delta < 2 ? delta : 2;
// 控制整个分组旋转
parent.rotation.y += -0.02 * delta;
...
renderer.clear()
renderer.render(scene, camera);
}
这样我们场景的旋转就完成了。
动态调整模型顶点位置
- 遍历物体数组,缓存一些数据
//! 根据动态或者静态模型调整,每个模型的顶点位置
for (var j = 0; j < meshes.length; j++) {
data = meshes[j]
mesh = data.mesh
vertices = data.vertices
vertices_tmp= data.vertices_tmp
vl = data.vl
// 对物体做处理
...
}
静态物体
- 如果是静态物体,跳过不做处理
//! 如果是静态模型,跳过
if(!data.dynamic) {
continue;
}
根据开始时间,判断是否开始动画
- 如最开始的时候是没有移动的
- 每个物体都缓存了开始时间,每帧开始时间减1,
- 当开始时间小于0,并且没有运动,设置物体向下运动
//! 最开始的时候,没有移动,设置移动,向下
if(data.start > 0) {
data.start -= 1 // 每帧减1
}else {
// 开始动画
if(!data.started) {
data.direction = -1; // 向下运动
data.started = true
}
}
设置粒子是向下还是向上运动
- 遍历顶点,通过判断物体是向上还是向下运动,做对应的处理
遍历顶点
for (let i = 0; i < vl; i++) {
p = vertices[i]; // 顶点
vt = vertices_tmp[i]; // 顶点缓存的数据
// 向上向下运动处理
...
}
向下运动
- 向下运动主要是顶点的
y坐标移动到 0 的位置 - 如果
y大于0,需要向下运动 - 如果已经小于等于0,说明已经到达底部,记录到达底部顶点数
// 向下运动
if(data.direction < 0){
if (p.y > 0) {
p.x += 1.5 * (0.5 - Math.random()) * data.speed * delta;
// 向下的概率明显大于向上的概率,所有整个人物粒子总有一个时刻是向下的
p.y += 3.0 * (0.15 - Math.random()) * data.speed * delta;
p.z += 1.5 * (0.5 - Math.random()) * data.speed * delta;
} else {
// 默认为0静止的, x,y,z,down,up
if (!vt[3]) {
vt[3] = 1; // 向下运动
data.down += 1; // 到达底部的顶点数
}
}
}
- x,z控制粒子左右前后摇摆
- y控制上下移动距离,由于是随机数,所以会有向上移动的粒子
0.15 - Math.random()的范围是[0.15, -0.75],向上移动概率是15%,向下移动概率是75%- 所以粒子整体是向下移动的
这个时候我们可以让粒子向下运动了,我们还需要告诉threejs,我更新了几何体的顶点信息
mesh.geometry.verticesNeedUpdate = true;
我们的向下运动就完成了
向上移动
- 向上运动我们需要把落到0位置的粒子,移动回原来的高度,需要计算落地点到原始位置的距离
- 通过让落地点的粒子逐渐靠近原始位置,来达到向上运动的效果
// 向上运动
if (data.direction > 0) {
// 计算落地点到原始位置的距离
d = Math.abs(p.x - vt[0]) + Math.abs(p.y - vt[1]) + Math.abs(p.z - vt[2]);
if (d > 1) {
// 越来越向vt[0]靠近,也就是原始位置
p.x += -(p.x - vt[0]) / d * data.speed * delta * (0.85 - Math.random());
p.y += -(p.y - vt[1]) / d * data.speed * delta * (1 - Math.random());
p.z += -(p.z - vt[2]) / d * data.speed * delta * (0.85 - Math.random());
} else {
// 默认up为0静止的, x,y,z,down,up
if (!vt[4]) {
vt[4] = 1; // 向上移动
data.up += 1; // 到达点对应位置的顶点数
}
}
}
- 有我们向下运动时,粒子随机的落地点不确定,所以xyz都要回到原始位置
-(p.x - vt[0]) / d每次移动总距离的一部分
由于第一次是向下运动,我们还没有告诉粒子什么时候向上运动,也就是direction什么时候大于0,接下来我们处理下这个逻辑
向下或向上移动完成切换运动
- 当向上或向下顶点数等于总顶点数,让停留时间开始计算,每次减1
- 停留时间结束后,就可以切换运动了
//! 如果是向下移动完成,那么休息一会,再向上移动
// 如果向下的顶点数等于总顶点数,就可以向上移动了
if (data.down === vl) {
if (data.delay === 0) { // 等待时间结束
data.direction = 1;
data.speed = 10;
data.down = 0;
data.delay = 300;
for (let i = 0; i < vl; i++) {
vertices_tmp[i][3] = 0;
}
} else { // 等待时间每次减1
data.delay -= 1;
}
}
//! 如果是向上移动完成,那么休息一会,再向下移动
if (data.up === vl) {
if (data.delay === 0) {
data.direction = -1;
data.speed = 10;
data.up = 0;
data.delay = 300;
for (let i = 0; i < vl; i++) {
vertices_tmp[i][4] = 0;
}
} else {
data.delay -= 1;
}
}
这样,我们的粒子运动效果就完成啦。 codepen示例代码