时间基 vs 帧基动画对比
实际对比
左边是帧基动画(固定每帧旋转 2π/60 弧度,以60Hz为基准一秒转一圈),右边是时间基动画(2π 弧度/秒,一秒转一圈)。
帧基动画 (Frame-based)
Loading...
60Hz: 1秒/圈 | 120Hz: 0.5秒/圈 | 240Hz: 0.25秒/圈
时间基动画 (Time-based)
Loading...
任何刷新率: 恒定1秒/圈
核心区别
帧基动画
// 每帧旋转固定角度
const autoRotationSpeed = Math.PI * 2 / 60 // 60Hz基准:一秒转一圈
function animate() {
requestAnimationFrame(animate)
loadedModel.rotation.y += autoRotationSpeed
renderer.render(scene, camera)
}问题:
- 60Hz 屏幕:每秒旋转约 2π 弧度(1圈)
- 120Hz 屏幕:每秒旋转约 4π 弧度(2圈,速度翻倍)
- 240Hz 屏幕:每秒旋转约 8π 弧度(4圈,速度翻4倍)
时间基动画
// 根据实际时间间隔计算旋转量
const autoRotationSpeed = Math.PI * 2 // 2π 弧度/秒(一秒转一圈)
let lastTime = performance.now()
function animate() {
requestAnimationFrame(animate)
const currentTime = performance.now()
const deltaTime = Math.min((currentTime - lastTime) / 1000, 0.1) // 封顶 100ms
lastTime = currentTime
loadedModel.rotation.y += autoRotationSpeed * deltaTime
renderer.render(scene, camera)
}关键代码对比
帧基版本
let animationId: number
const autoRotationSpeed = Math.PI * 2 / 60 // 60Hz基准:一秒转一圈
const animate = () => {
animationId = requestAnimationFrame(animate)
if (loadedModel) {
loadedModel.rotation.y += autoRotationSpeed
}
renderer.render(scene, camera)
}
animate()时间基版本
let animationId: number
let lastTime = performance.now()
const autoRotationSpeed = Math.PI * 2 // 2π 弧度/秒(一秒转一圈)
const animate = () => {
animationId = requestAnimationFrame(animate)
const currentTime = performance.now()
const deltaTime = Math.min((currentTime - lastTime) / 1000, 0.1) // 封顶 100ms
lastTime = currentTime
if (loadedModel) {
loadedModel.rotation.y += autoRotationSpeed * deltaTime
}
renderer.render(scene, camera)
}
animate()时间基的额外保护
const deltaTime = Math.min((currentTime - lastTime) / 1000, 0.1)Math.min(..., 0.1) 封顶非常重要:
- 标签页切换:用户切走时 rAF 暂停,切回来时时间差可能很大
- 显示器刷新率切换:外接显示器从 60Hz 切换到 144Hz
- 系统休眠唤醒:长时间休眠后恢复
适用场景
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 3D 模型旋转 | 时间基 | 需要一致的速度体验 |
| 游戏动画 | 时间基 | 物理模拟依赖真实时间 |
| UI 微交互 | 帧基可接受 | 轻微速度差异不明显 |
| 数据可视化图表 | 时间基 | 需要精确的动画时长 |
总结
// ❌ 帧基 - 速度依赖刷新率(60Hz下1秒/圈,240Hz下0.25秒/圈)
rotation += Math.PI * 2 / 60
// ✅ 时间基 - 速度恒定(任何刷新率都是1秒/圈)
const deltaTime = (currentTime - lastTime) / 1000
rotation += Math.PI * 2 * deltaTime
// ⚠️ 时间基 + 封顶 - 防止标签页切换后跳跃
const deltaTime = Math.min((currentTime - lastTime) / 1000, 0.1)
rotation += Math.PI * 2 * deltaTime结论:除非明确知道动画不需要恒定速度,否则都应该使用时间基动画。
Last updated on