上节实现了小鱼吐泡泡:

这节我们继续来实现游动:
我们安装下 gsap 或者 tween.js 来做缓动动画。
这次我们用 gsap:
npm install --save gsap
给 fish 和 emitter 个名字:


fish2.name = 'fish';
particles.emitter.name = 'emitter';
fish 移动的时候同步修改 bubble 的位置:

这里要 * scale
import gsap from 'gsap';
import bubbles from './bubbles';
gsap.to(fish2.position, {
duration: 100,
z: -300,
onUpdate: () => {
const emitter = bubbles.getObjectByName('emitter');
emitter.position.z = fish2.position.z * 100;
}
});
看下效果:

这样,鱼的游动和气泡的位置同步就完成了。
我们改下相机位置:

把 OrbitControls 注释掉,它会把 lookAt 重置:

camera.position.set(800, 500, -500);
camera.lookAt(0, 300, -500);

现在这个视角就好多了。
小鱼游的有点远,改小一点:

duration: 5,
z: -10,
因为我们放大了 100 倍,所以这就是 5s 内 z 从 0 运动到 -1000 的位置。

这样就不会游到视野之外了。
不过气泡位置不大对,z 应该再加上鱼身宽度的一半。
获取鱼身宽度可以用包围盒:

const box3 = new THREE.Box3();
box3.setFromObject(fish2);
const size = box3.getSize(new THREE.Vector3());
emitter.position.z = fish2.position.z * 100 - size.x * 100 / 2;

现在就对了。
然后游到头之后再游回来:
const tl = gsap.timeline();
fish2.rotation.y = 0;
tl.to(fish2.position, {
duration: 5,
z: -10,
onUpdate: () => {
const emitter = bubbles.getObjectByName('emitter');
emitter.visible = true;
emitter.position.z = fish2.position.z * 100 - size.x * 100 / 2;
}
}).to(fish2.rotation, {
y: Math.PI,
duration: 1,
onUpdate: () => {
const emitter = bubbles.getObjectByName('emitter');
emitter.visible = false;
}
}).to(fish2.position, {
z: 0,
duration: 5,
onUpdate: () => {
const emitter = bubbles.getObjectByName('emitter');
emitter.visible = true;
emitter.position.z = fish2.position.z * 100 + size.x * 100 / 2;
}
}).to(fish2.rotation, {
y: 0,
duration: 1,
onUpdate: () => {
const emitter = bubbles.getObjectByName('emitter');
emitter.visible = false;
}
}).repeat(Infinity);
用 gsap 的 timeline 来串联动画。
首先往右边游,游到头转身,再游回来,之后再转身。
转身的时候把气泡隐藏。
看下效果:

这样鱼就能无限反复游了。
发现我们忘记加气泡的帧动画了,加一下:

particles.addBehavior(
new FrameOverLife(
new PiecewiseBezier(
[
[new Bezier(36, 39, 42, 44), 0]
]
)
)
);

这个帧动画的变化不是很明显。
去掉坐标轴,看下整体效果:

效果不错。
案例代码上传了小册仓库
总结
这节我们实现了小鱼来回游动的效果。
用 gsap 实现的串联的动画,首先游动到一个位置,接下来转身,之后再游回去,然后再转身,不断循环执行这个动画就可以实现来回游动的效果。
在转身的时候气泡位置不好计算,我们直接把他隐藏,转身完之后再显示就好了。
这样,小鱼吐泡泡的效果就完成了,用到了粒子效果、gsap 串联动画、包围盒、骨骼动画,是一个比较综合的小实战。