requestAnimationFrame 是浏览器原生动画API,按屏幕刷新率执行且自动暂停;与 setTimeout 不同,它需递归调用、基于时间戳计算位移、及时 cancel 以避免泄漏。
requestAnimationFrame 是浏览器提供的用于驱动动画的原生 API,它不是定时器,而是告诉浏览器“你下次重绘前,帮我执行这个函数”。相比 setTimeout 或 setInterval,它更省电、更顺滑、自动
适配屏幕刷新率(通常是 60fps),且在标签页不可见时会自动暂停。
常见错误是把它当“更高级的 setTimeout”来用,比如写成 requestAnimationFrame(() => { /* 一次就完事 */ }) —— 这样只会执行一帧,根本动不起来。
真正能动起来的动画,必须在回调里**递归调用自己**。这是最常漏掉的关键点。
requestAnimationFrame(animate)
cancelAnimationFrame 停止pos += 2),应基于上一帧时间戳计算真实经过毫秒数,避免丢帧导致速度漂移let animationId = null; let lastTime = 0;function animate(timestamp) { if (!lastTime) lastTime = timestamp; const deltaTime = timestamp - lastTime; lastTime = timestamp;
// 示例:让元素每秒移动 100px const speed = 100; // px/s element.style.transform =
translateX(${speed * deltaTime / 1000}px);animationId = requestAnimationFrame(animate); }
animationId = requestAnimationFrame(animate);
不手动停止会导致内存泄漏和后台持续耗电,尤其在组件卸载、页面切换或用户暂停动画时。
cancelAnimationFrame(animationId) 即可中断当前排队中的帧animationId 是你上次 requestAnimationFrame 返回的值,赋值时机不能错useEffect cleanup 或 beforeUnmount 钩子中调用取消逻辑现代浏览器都支持 requestAnimationFrame,但 IE10+ 才有,且需要带前缀 webkitRequestAnimationFrame(已基本可忽略)。真正容易被忽略的是帧率控制与精度问题:
timestamp 参数是高精度时间(单位毫秒,小数点后三位),别用 Date.now() 替代,后者精度低且不受浏览器节流影响offsetTop)、复杂计算;批量读写分离(先读所有 offset,再统一写 style)if (timestamp % 33 这类取模——它不准确;应记录上一次渲染时间,差值不够就跳过本次
动画不是写得越勤快越好,而是刚好卡在浏览器重绘节奏上。多一帧没用,少一帧就卡。