Skip to content

264. 全屏滚动实战:星系 3D 科普网站(四)

Published:

前面把星系和 div 部分都画完了:

2025-09-09 10.39.58.gif

这节加上滚动的交互:

首先我们监听滚动事件,拿到当前是第几屏:

image.png

let curPageNo;
window.addEventListener('scroll', () => {
    const pageNo = Math.round(window.scrollY /window.innerHeight);

    if(pageNo !== curPageNo) {
        curPageNo = pageNo;
        console.log(pageNo);
    }
});

export function getCurPageNo() {
    return curPageNo;
}

2025-09-09 11.06.39.gif

可以看到,能拿到正确的页码。

然后我们在滚动到每一页的时候,改一下相机位置:

就像前面控制人物行走时一样:

2025-09-21 21.38.37.gif

把相机加到和星球一个 group 里就行了。

把星球放到一个个 group 里:

image.png

注释掉之前的位置。

把行星放到一个个 group 里。

const planetGroup = new THREE.Group();
planetGroup.name = 'planetGroup' + index;
planetGroup.add(planet);
group.add(planetGroup);

为什么要注释掉位置呢?

因为有了 group,位置就是相对于局部坐标系了。

我们加个 AxesHelper 看看:

image.png

const helper = new THREE.AxesHelper(1000);
planetGroup.add(helper);

改变位置的不再是行星,而是整个 group:

image.png

顺便把太阳调小一点:

image.png

看下效果:

2025-10-10 18.58.23.gif

可以看到,每个星球都有一个单独的 group 的局部坐标系。

然后我们把 camera 放到每个 group 里,并调整下位置,滚动切换页码的时候调整下 group:

image.png

window.addEventListener('scroll', () => {
    const pageNo = Math.round(window.scrollY /window.innerHeight);

    if(pageNo !== curPageNo) {
        curPageNo = pageNo;
        console.log(pageNo);
        if(curPageNo === 0) {
            return;
        }
        camera.parent?.remove(camera);
        const targetGroup = mesh.getObjectByName('planetGroup' + (curPageNo - 1));
        targetGroup.add(camera);

        const planet = targetGroup.children[0];

        const r = planet.geometry.parameters.radius;

        camera.position.set(0, 0, r * 5);
    }
});

把 scroll 的处理逻辑拿到 init 函数里,这样可以访问到 camera。

第 0 页是介绍,不用处理,从第 1 开始依次是太阳、水星。。。

拿到对应的 group,把 camera 放里面,并且改一下相机位置在半径 * 5 的位置。

去掉最开始的相机位置:

image.png

看下效果:

2025-10-10 19.08.03.gif

这样,滚动到对应页码的时候,相机就会定位到对应的星球。

去掉所有 AxesHelper,把星轨透明度调高一点,并且点取得更密一点:

image.png

看下效果:

2025-10-10 19.15.18.gif

这样,太阳系科普网站就完成了。

案例代码上传了小册仓库

总结

这节我们实现了滚动切换星球到视野中央的功能。

滚动的页码计算之前讲过,就是用 window.scrollY 和 window.innerHeight 来计算。

定位星球位置到中央,可以把相机加到星球所在的 group 中,让它跟着 group 一起动。

两者结合,就可以实现滚动页面,切换视野中的行星的功能。

这算是一个综合性的小实战,以后遇到需要滚动控制一些交互效果的需求,就可以用这种方式实现。

评论