上节实现了 3D 全屏滚动页面的基本效果:

这节我们继续完善。
首先,我们可以算出滚动的每一屏:

window.addEventListener('scroll', () => {
const pageNo = Math.round(window.scrollY /window.innerHeight);
console.log(pageNo);
});
这里用 Math.round 来做四舍五入

用滚动的距离除以每一屏的高度,就可以算出当前是第几屏或者叫第几页。
然后在切换页码的时候做一下标题的动画。
安装 gsap 来做动画:
pnpm install --save gsap

let curPageNo;
window.addEventListener('scroll', () => {
const pageNo = Math.round(window.scrollY /window.innerHeight);
if(pageNo !== curPageNo) {
curPageNo = pageNo;
gsap.to('.section' + (pageNo + 1) + ' h1', {
rotate: '+=360',
duration: 1
});
}
});
如果页数变化了,就让当前页的标题转一圈。
+=360 是在之前角度上加 360 度的意思。

这样,切换页码时的标题动画就做完了。
然后我们在切换页码的时候,加一下 3D 场景中对应物体的动画:

gsap.to(mesh.children[pageNo].scale, {
x: 0.3,
y: 0.3,
z: 0.3,
yoyo: true,
repeat: 1,
duration: 0.5,
ease: 'bounce.in'
})
让对应的物体做一下放缩的动画。
repeat 一次是 0,两次是 1,这里要来回动画,所以是 1
这里的缓动动画函数可以在这里调效果:
https://gsap.com/docs/v3/Eases/


试一下:

现在的交互还是太单调了,只有滚动的交互,我们再加上鼠标交互:

window.addEventListener('mousemove', (e) => {
const moveX = (e.clientX / window.innerWidth) - 0.5;
camera.position.x += moveX * 300 - camera.position.x;
});
监听 mousemove 事件。
e.clientX 拿到鼠标位置距离屏幕左边的距离,除以屏幕宽度,就是 -1 到 1 的值。
因为物体是在中央,所以 -0.5 就是 -0.5 到 0.5 范围的值,也就是距离中央偏离了多少。
然后这个值放大一下减去相机原来的位置,就是偏移量。
看下效果:


这样,页面交互就更丰富了。
案例代码上传了小册仓库
总结
这节我们完善了下 3D 全屏滚动网站的交互。
首先,在 scroll 的时候,通过 window.scrollY 和 window.innerHeight 的比值算出当前是第几页。
如果页面切换了,就给标题加一个旋转动画,并且给对应的物体加一个放缩动画。
然后我们又加上了鼠标的交互,mousemove 的时候,根据鼠标移动的距离来移动相机,实现鼠标控制 3D 物体的效果。
当然,我们这里只是学习原理,做的效果比较简单,但复杂的效果也都是基于 scroll、mousemove 来改变 camera 位置实现的。