动画计时——使用基于时间的循环入门
构建一个小型动画循环,使用基于时间(增量)的更新,模拟 requestAnimationFrame,并添加简单的缓动效果
动画计时——使用基于时间的循环入门 是 CoddyKit 上的免费 JavaScript Academy 课时。 这是第 3 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 JavaScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 JavaScript Academy 课程共包含 3 节课。
为什么使用基于时间的动画
目标:通过基于时间的更新驱动平滑运动。
- 模拟requestAnimationFrame
- 测量时间增量
- 以单位/秒的速度移动
- 添加微小的缓动

模拟 rAF
这里没有 DOM,因此我们使用大约每 16 毫秒运行一次的setTimeout来模拟requestAnimationFrame。
// requestFrame: simulate ~60 FPS with setTimeout
function requestFrame(fn) {
// ~16ms between frames for ~60fps
return setTimeout(function () { fn(Date.now()); }, 16);
}
function cancelFrame(id) {
clearTimeout(id);
}
// Tiny demo: schedule one frame
requestFrame(function (ts) {
console.log("frame at", ts);
});

时间增量基础
在每一帧计算时间增量:(now − last) / 1000。这样可以根据实际时间调整运动。
// Time-based loop: compute delta time in seconds
let running = true;
let last = Date.now();
function loop() {
if (!running) { return; }
const now = Date.now();
const dt = (now - last) / 1000; // seconds
last = now;
// Print a tiny delta preview
console.log("dt", dt.toFixed(3), "s");
requestFrame(loop);
}
// Start the loop and stop after a few frames
loop();
setTimeout(function () {
running = false;
console.log("stop");
}, 60);

每秒单位数
使用speed × 时间增量更新位置,使运动在不同帧率下保持一致。
// Move with a speed in units/second
let x = 0; // position
const speed = 50; // units per second
let lastT = Date.now();
let frames = 0;
function moveLoop() {
const now = Date.now();
const dt = (now - lastT) / 1000;
lastT = now;
// Distance = speed * time
x = x + speed * dt;
frames = frames + 1;
console.log("x", Math.round(x));
if (frames < 5) {
requestFrame(moveLoop);
} else {
console.log("final x", Math.round(x));
}
}
moveLoop();

微型缓动演示
缓动会塑造运动曲线。这里的easeOutQuad开始时较快,接近结束时逐渐变慢。
// Ease from 0 to 1 over duration using easeOutQuad
function easeOutQuad(t) {
// t in [0,1]
return 1 - (1 - t) * (1 - t);
}
async function tween(durationMs) {
let start = Date.now();
let id = null;
function step() {
const now = Date.now();
const t = Math.min(1, (now - start) / durationMs);
const eased = easeOutQuad(t);
console.log("eased", eased.toFixed(2));
if (t < 1) {
id = requestFrame(step);
} else {
cancelFrame(id);
console.log("done");
}
}
step();
}
tween(60);

体验固定步长
固定步长可以稳定逻辑;如果某一帧延迟,累加器会负责追赶。
// Fixed-step taste: step a physics tick and catch up using an accumulator
let acc = 0; // accumulated time
const step = 1 / 30; // 30 FPS fixed step in seconds
let lastTime = Date.now();
let value = 0;
let fixedFrames = 0;
function fixedLoop() {
const now = Date.now();
const dt = (now - lastTime) / 1000;
lastTime = now;
acc = acc + dt;
// Run zero or more fixed steps to catch up
while (acc >= step) {
// Update logic at a fixed rate
value = value + 1; // pretend physics
acc = acc - step;
}
console.log("value", value);
fixedFrames = fixedFrames + 1;
if (fixedFrames < 5) {
requestFrame(fixedLoop);
} else {
console.log("fixed stop");
}
}
fixedLoop();

时间增量与固定步长测验
快速检查:为什么需要时间增量?

回顾
回顾:您使用setTimeout模拟了 rAF,测量了时间增量,以单位/秒的速度移动,添加了缓出效果,并体验了带累加器的固定步长。

常见问题解答
「动画计时——使用基于时间的循环入门」课时是免费的吗?
是的 — 「动画计时——使用基于时间的循环入门」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 JavaScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 JavaScript Academy 课程共包含 3 节课。
「动画计时——使用基于时间的循环入门」这节课中我会学到什么?
构建一个小型动画循环,使用基于时间(增量)的更新,模拟 requestAnimationFrame,并添加简单的缓动效果 你通过在浏览器中直接运行的动手代码来练习 JavaScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 JavaScript Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 JavaScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 3 节。
「动画计时——使用基于时间的循环入门」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 JavaScript Academy 课中编写并运行代码吗?
能。每节 JavaScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- setTimeout、setInterval 与计时器漂移
- 防抖与节流——手动实现
- 动画计时——使用基于时间的循环入门