Three.js的3D模型旋转时为什么会卡顿?

端木义霞 阅读 54

最近在用Three.js做3D图表时遇到个问题,当给模型添加自旋动画后,页面滚动就变得卡顿了。模型本身也不算太大,是用glTF加载的,旋转代码也按官方示例写的:


function animate() {
    requestAnimationFrame(animate);
    mesh.rotation.x += 0.01;
    mesh.rotation.y += 0.01;
    renderer.render(scene, camera);
}

试过把动画改成用CSS,但模型位置就对不准了。也尝试过用THREE.LoopEuler参数,但好像没什么变化。难道是渲染设置哪里没配好?

我来解答 赞 7 收藏
二维码
手机扫码查看
2 条解答
Mc.梓轩
Mc.梓轩 Lv1
这个问题大概率不是旋转逻辑的问题,而是渲染和性能控制没做校验。你的 animate 函数一直在疯狂执行 requestAnimationFrame,即使页面不可见或者用户根本没在看,照样每秒跑60次以上,GPU占用直接拉满,页面滚动当然卡。

首先你得做帧率节流和可见性检测。别直接无脑 requestAnimationFrame 嵌套调用。加个时间判断,比如控制旋转更新频率:

let lastTime = 0;
function animate(timestamp) {
if (!lastTime || timestamp - lastTime > 30) { // 控制在 ~30fps
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.01;
renderer.render(scene, camera);
lastTime = timestamp;
}
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);


其次要做页面可见性校验。用户切到别的标签页时,动画完全没必要跑。加个监听页面可见性的钩子:

document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
// 页面不可见时暂停动画
cancelAnimationFrame(animId);
} else {
animId = requestAnimationFrame(animate);
}
});


另外检查下你的 glTF 模型有没有做纹理压缩和几何体简化。可以用 THREE.DRACOLoader 解压 Draco 压缩的网格,加载时显式启用:

gltfLoader.setDRACOLoader(dracoLoader)

还有,确认 renderer 开启了抗锯齿和自动清除缓存:

const renderer = new THREE.WebGLRenderer({
antialias: true,
powerPreference: "high-performance", // 显卡优先
stencil: false,
depth: false
});
renderer.autoClear = true;


最后提醒一点,别在主线程干太多事。如果图表数据量大,考虑把旋转逻辑拆到 Web Worker 或者用 CSS 3D 层级叠加(但要注意 z-index 和 transform-origin 对齐)。不过 Three.js 本身不推荐全丢给 CSS,容易失控。

先加上时间间隔控制和 visibility 校验,90% 的卡顿都能缓解。
点赞 3
2026-02-12 09:02
司马桂香
这个问题很常见,卡顿的根源在于你在 requestAnimationFrame 中直接修改了模型的旋转值,这种做法会破坏 Three.js 的渲染调度,导致每一帧都强制重绘。官方推荐的做法是通过 Clock 类来管理动画时间,而不是用累加的方式。

你应该改成这样:

const clock = new THREE.Clock();

function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();

mesh.rotation.x += 0.5 * delta;
mesh.rotation.y += 0.5 * delta;

renderer.render(scene, camera);
}


这样可以让旋转速度与帧率解耦,避免因帧率波动导致动画卡顿。

另外,如果你的模型是静态的,只有旋转动画,建议开启 renderer.autoClear = false 并手动控制渲染频率,或者使用 OrbitControls 自带的自动更新机制。如果你不需要每帧都渲染,可以限制 animate() 的调用频率。

最后检查一下你的 glTF 是否带了大量骨骼或动画,这些也会影响性能。用 GLTFLoader 加载时记得启用 draco 压缩(如果用了),能有效降低模型资源占用。
点赞 6
2026-02-03 19:02