在 Three.js 中,动画循环是实现动态场景的核心机制。requestAnimationFrame 是浏览器提供的 API,用于在下一次重绘前执行回调函数,通常以每秒 60 帧的频率运行。
javascript
function animate() {
requestAnimationFrame(animate);
// 更新场景对象
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
// 渲染场景
renderer.render(scene, camera);
}
animate();
为了避免不同刷新率设备上的动画速度差异:
javascript
let clock = new THREE.Clock();
let delta;
function animate() {
requestAnimationFrame(animate);
delta = clock.getDelta(); // 获取上一帧到当前帧的时间间隔
// 使用时间增量使动画帧率独立
cube.rotation.x += 1 * delta;
cube.rotation.y += 1 * delta;
renderer.render(scene, camera);
}
javascript
// 初始化
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建一个立方体
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
// 动画循环变量
const clock = new THREE.Clock();
let previousTime = 0;
// 动画循环
function animate(_currentTime) {
requestAnimationFrame(animate);
// 计算时间增量
const elapsedTime = clock.getElapsedTime();
previousTime = currentTime;
// 更新动画
cube.rotation.x = elapsedTime * 0.5; // 基于总时间旋转
cube.rotation.y = elapsedTime * 0.3;
// 添加脉动效果
cube.scale.setScalar(1 + Math.sin(elapsedTime) * 0.2);
// 渲染
renderer.render(scene, camera);
}
// 启动动画循环
animate();
javascript
class AnimationLoop {
constructor(renderer, scene, camera) {
this.renderer = renderer;
this.scene = scene;
this.camera = camera;
this.clock = new THREE.Clock();
this.delta = 0;
this.fps = 60;
this.interval = 1000 / this.fps;
this.then = Date.now();
this.isRunning = false;
// 动画更新函数数组
this.updaters = [];
}
addUpdate(fn) {
this.updaters.push(fn);
}
start() {
if (this.isRunning) return;
this.isRunning = true;
this.animate();
}
stop() {
this.isRunning = false;
}
animate = () => {
if (!this.isRunning) return;
requestAnimationFrame(this.animate);
const now = Date.now();
this.delta = now - this.then;
// 控制帧率
if (this.delta > this.interval) {
this.then = now - (this.delta % this.interval);
// 执行所有更新函数
this.updaters.forEach(update => {
update(this.clock.getDelta());
});
this.renderer.render(this.scene, this.camera);
}
}
}
// 使用
const loop = new AnimationLoop(renderer, scene, camera);
loop.addUpdate((delta) => {
cube.rotation.x += 0.5 * delta;
cube.rotation.y += 0.3 * delta;
});
loop.start();
javascript
function animate() {
requestAnimationFrame(animate);
// 只在场景需要更新时才渲染
if (sceneNeedsUpdate) {
updateScene();
renderer.render(scene, camera);
sceneNeedsUpdate = false;
}
}
// 或者在变化时手动触发
function onModelLoaded() {
sceneNeedsUpdate = true;
}
javascript
import * as TWEEN from '@tweenjs/tween.js';
function animate(time) {
requestAnimationFrame(animate);
TWEEN.update(time); // 更新所有补间动画
// 更新其他动画
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
// 创建补间动画
new TWEEN.Tween(cube.position)
.to({ x: 2, y: 1, z: 0 }, 1000)
.easing(TWEEN.Easing.Quadratic.Out)
.start();
始终使用时间增量:确保动画在所有设备上速度一致
清理资源:停止动画循环时取消 requestAnimationFrame
性能监控:使用 stats.js 监控帧率
暂停/恢复功能:实现动画循环的暂停和恢复
javascript
let animationId = null;
let isPaused = false;
function animate() {
animationId = requestAnimationFrame(animate);
if (isPaused) return;
// 动画逻辑
renderer.render(scene, camera);
}
function pauseAnimation() {
isPaused = true;
}
function resumeAnimation() {
isPaused = false;
}
function stopAnimation() {
cancelAnimationFrame(animationId);
animationId = null;
}
这些模式可以帮助你创建流畅、高效的 Three.js 动画循环。