Three.js - 动画
Three.js - 动画
Section titled “Three.js - 动画”静态 3D 场景虽然有趣,但动画才是真正赋予它们生命力的关键。在 Three.js 中,动画是通过创建渲染循环 (render loop) 来实现的,该循环在每一帧更新场景并重新渲染,从而产生运动的错觉。
使用 requestAnimationFrame 的渲染循环
Section titled “使用 requestAnimationFrame 的渲染循环”创建高效动画循环的标准、浏览器原生方法是使用 window.requestAnimationFrame() 方法。它告诉浏览器您希望执行动画,并请求浏览器在下一次重绘之前调用一个指定的函数来更新动画。这比使用 setInterval 效率高得多,因为它允许浏览器优化渲染,在非活动标签页中暂停动画,并与显示器的刷新率同步。
function tick() { // 在这里放置更新场景中对象的代码
// 渲染场景 renderer.render(scene, camera);
// 安排下一帧 window.requestAnimationFrame(tick);}
// 启动循环tick();帧率无关动画
Section titled “帧率无关动画”一个常见的错误是在每一帧中以固定量更新对象的属性,例如 cube.rotation.y += 0.01;。这会产生一个问题:动画在高刷新率显示器(例如 144Hz)上会运行得更快,而在标准显示器(60Hz)上则会更慢。
为了解决这个问题,我们必须使用帧之间经过的时间来计算更新。Three.js 为此提供了一个便捷的 THREE.Clock 工具。
import * as THREE from 'three';
const clock = new THREE.Clock();
function tick() { // 获取自上一帧以来经过的时间 const deltaTime = clock.getDelta();
// 获取自时钟启动以来经过的总时间 const elapsedTime = clock.getElapsedTime();
// 根据经过时间更新对象旋转,以实现平滑、一致的速度 // cube.rotation.y = elapsedTime * Math.PI; // 每秒旋转 180 度
// 使对象沿圆形路径移动 cube.position.x = Math.cos(elapsedTime) * 5; cube.position.z = Math.sin(elapsedTime) * 5;
renderer.render(scene, camera); window.requestAnimationFrame(tick);}
tick();使用补间 (Tweening) 库实现高级动画
Section titled “使用补间 (Tweening) 库实现高级动画”对于更复杂的、基于时间线的动画(例如,在 2 秒内将对象从 A 点移动到 B 点并带有特定的缓动效果),专用的补间库 (tweening library) 是无价的。虽然原始教程使用了 Tween.js,但一个非常流行且强大的行业标准是 GSAP (GreenSock Animation Platform)。它以其性能、灵活性和出色的文档而闻名。
让我们看看如何将 GSAP 集成到我们的项目中。
npm install gsapGSAP 可以直接动画任何 JavaScript 对象的属性,包括 Three.js 对象,例如 mesh.position、mesh.rotation 或 material.color。
import gsap from 'gsap';
// 在 1 秒内动画立方体的 Y 轴位置gsap.to(cube.position, { duration: 1, delay: 0.5, y: 5 });
// 以弹性缓动效果向下动画gsap.to(cube.position, { duration: 1.5, delay: 1.5, y: 0, ease: 'elastic.out(1, 0.3)' });GSAP 的一个主要优点是您不需要在渲染循环中调用更新函数。它管理自己的内部计时器,使动画代码非常简洁和声明式。
动画最佳实践
Section titled “动画最佳实践”- 性能:避免在渲染循环内部创建新对象(如
new THREE.Vector3())或执行大量计算。尽可能在循环外部完成设置。 - 调试:使用
console.log(elapsedTime)检查时间,或使用stats.js库监控场景的帧率 (FPS)。 - 控制:对于复杂的序列动画,可以使用 GSAP 等库;但对于连续的、程序性的动画,如波浪、漂浮或恒定旋转,则应利用
tick函数和THREE.Clock。 - 用户体验:如果用户对动画敏感,应始终提供暂停或减少动画的方式。考虑使用
(prefers-reduced-motion)CSS 媒体查询来默认禁用非必要的动画。
迷你项目构想:动画太阳系
Section titled “迷你项目构想:动画太阳系”实践这些概念的一个好方法是构建一个简单的太阳系。
- 为太阳创建一个球体,并为行星创建几个较小的球体。
- 在
tick函数中,使用elapsedTime和Math.sin()和Math.cos()使行星以不同的速度和距离绕太阳公转。 - 使用
planet.rotation.y = elapsedTime * speed;使行星围绕自己的轴旋转。 - 挑战:当用户点击某个行星时,使用 GSAP 动画摄像机,使其从广角视图移动并聚焦到该行星。