Sunday面试指南

requestAnimationFrame 和 setTimeout 有什么区别?为什么动画不应该固定每帧移动几像素?

下面是一段教学用的模拟面试。

🧑‍💻 面试官:页面动画用 setTimeout 每隔 16 毫秒移动一次,可以吗?

🙋‍♂️ 我:可以做,但定时器不一定准时,也不一定对上屏幕刷新。

🧑‍💻 面试官:那换成 requestAnimationFrame,每次移动 5 像素呢?

🙋‍♂️ 我:高刷新率屏幕回调更多,可能会移动得更快。

🧑‍💻 面试官:怎么让动画在不同屏幕上都用一秒完成?标签页切到后台再回来,又应该显示哪里?

先确定动画的「时间进度」,再安排什么时候更新画面。回调次数不能代替真实经过时间。

面试速答(60 秒版)

setTimeout 表示经过指定延迟后,回调有机会被调度,不承诺准确在那个时间执行。requestAnimationFrame 则让浏览器在一次合适的绘制前执行回调,更适合更新动画画面。

rAF 通常跟着屏幕刷新节奏运行,不固定等于每 16 毫秒一次,而且只安排一轮,下一轮要再次请求。后台页面通常会暂停这类回调。

动画应该根据回调提供的时间戳计算进度,而不是每次固定移动几像素。这样即使回调间隔变化,同一个时间点也能得到同一个位置。至于后台回来要跳到当前进度,还是从暂停处继续,需要根据产品要求设计时间基准,不能让调度行为替你决定。

动画跟着时间,不跟着次数

图:动画跟着时间,不跟着次数。

知识点详解:让一个小球在一秒内移动 200 像素

固定步长,为什么会跟着屏幕变快?

假设咱们每收到一轮回调,就把小球向右移动 5 像素。完成 200 像素需要 40 轮。屏幕刷新更快,40 轮可能更早到来;页面很忙,40 轮也可能拖很久。

问题不在于 5 像素选得不好,而在于这段代码用「次数」定义速度。改成根据经过时间计算,移动关系就清楚了:经过 500 毫秒,走一半;经过 1000 毫秒,到终点。

每次走一样远,速度就会变

图:每次走一样远,速度就会变。

时间戳怎样参与计算?

把第一轮时间保存为起点,每轮算出经过时长,再除以总时长。进度超过 1 时截到 1,避免越过终点。rAF 文档说明了回调时间戳与不同刷新率下的注意事项。

下面是浏览器 TypeScript 示例。Python 没有同一浏览器帧 API;服务端定时器不能充当它的对应实现。

TypeScript

function move(ball: HTMLElement): () => void {
  let start: number | undefined;
  let frameId = 0;
  function frame(now: number) {
    start ??= now;
    const progress = Math.min((now - start) / 1000, 1);
    ball.style.transform = 'translateX(' + 200 * progress + 'px)';
    if (progress < 1) frameId = requestAnimationFrame(frame);
  }
  frameId = requestAnimationFrame(frame);
  return () => cancelAnimationFrame(frameId);
}

返回的函数用于取消尚未执行的回调。组件退出时调用它,可以避免动画继续操作不再需要的元素。这里演示的是线性运动,不是通用动画库。

用 rAF 就不会卡了吗?

也不会。回调中的工作仍需要完成。如果一轮里计算很久、不断读写布局,浏览器可能错过绘制机会。rAF 帮你选择更新时机,没有增加主线程的处理能力。

因此,复杂动画可以优先考虑 CSS 动画或 Web Animations;确实需要逐帧计算,再使用 rAF。测量时看每轮耗时、掉帧和主线程长任务,不只看调用了哪个 API。

后台回来,时间应不应该继续算?

上面的例子保留第一轮起点,回到前台后会根据经过的时间直接计算位置,可能立即到终点。倒计时或按绝对时间展示的进度,可以需要这种行为。

如果是必须暂停的交互动画,则要记录暂停时长,从总经过时间里扣掉。两种行为都可能正确,但要先把需求说清楚。验证时除了正常播放,还要切后台、取消和重复启动。

面试官继续追问

rAF 是绘制完成后的通知吗?

不是,它用于安排绘制前的更新机会。不能把回调执行理解成用户已经看到了这一帧。

回调里再请求一次,会在同一帧不断执行吗?

不会变成一个同步循环;下一次请求等待后续的回调机会。仍要控制每轮工作量。

所有延迟任务都应该换成 rAF 吗?

不是。后台巡检、请求超时等不依赖显示节奏,而且后台暂停可能违背需求。按任务目的选择,不按名字先进程度选择。

面试速记卡

  • 定时器:延迟后获得调度机会,不保证准点。
  • rAF:配合绘制节奏,一次请求只安排一轮。
  • 进度:根据时间戳计算,不根据回调次数计算。
  • 性能:回调太重仍会掉帧,调度不是加速计算。
  • 后台:继续计时还是暂停,要明确时间模型。

公司面试真题

这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。

浏览公司面试真题 →
简历汪永久免费在线制作简历,模板直接套用、导出无水印,永久免费、下载免费,不需要付费解锁任何功能。去写简历