前端动画选哪个更好:requestAnimationFrame vs 定时器
做前端开发时,流畅的动画效果很重要,能明显提升用户体验。但实现动画的方法有很多,到底该选哪种呢?requestAnimationFrame 和定时器(setTimeout、setInterval)都是常用的动画实现方法,它们在工作原理、性能表现和使用场景上都有很大不同。
requestAnimationFrame 是什么?
这是浏览器专门为动画提供的接口。它告诉浏览器在下次页面重绘前执行你的动画代码。
基本用法很简单:
let requestId = requestAnimationFrame(callback);callback:浏览器在下次重绘前调用的函数
requestId:请求的标识号,可以用来取消动画
工作原理
浏览器运行时有个事件循环机制,处理各种任务。requestAnimationFrame 的回调函数会被放到浏览器的动画帧队列里,在重绘阶段执行。简单说,浏览器每次要更新画面时,会先执行这些动画函数,然后再绘制页面。
优点
与浏览器刷新同步:动画更新和页面重绘保持同步,避免不必要的重复绘制,性能更好
智能节能:当页面被隐藏或切到后台时,浏览器会自动暂停动画,节省资源
动画更流畅:帧率与浏览器刷新率匹配,通常是每秒60帧,动画效果很顺滑
缺点
兼容性问题:老版本浏览器可能不支持,需要做兼容处理
不能控制执行频率:执行间隔由浏览器决定,不能像定时器那样精确设置
定时器是什么?
主要通过 setTimeout 和 setInterval 来实现:
setTimeout:延迟指定时间后执行一次代码
let timeoutId = setTimeout(function, delay);setInterval:按固定时间间隔重复执行代码
let intervalId = setInterval(function, delay);工作原理
定时器在设定的时间后把回调函数放入执行队列,等待处理。但由于JavaScript是单线程的,如果当前有任务在执行,定时器的回调可能要等更久才能运行。
优点
简单好用:用法直观,容易理解
能控制频率:可以精确设置执行间隔,适合需要固定频率的场景
缺点
性能问题:如果回调函数执行太久,会阻塞后面代码,影响页面响应
时间不准:实际执行间隔可能比设定的长,导致动画卡顿
不会自动优化:即使页面看不见,定时器也会继续执行,浪费资源
两者对比
性能方面
requestAnimationFrame更好,它与浏览器重绘同步,动画流畅,而且在页面不可见时会自动暂停。
定时器可能因为JavaScript执行阻塞导致时间不准,影响动画效果,也没有优化机制。
适用场景
requestAnimationFrame:适合大多数动画场景,特别是需要流畅动画的地方,比如游戏、复杂交互动画。
定时器:适合简单的、对流畅度要求不高的动画,或者需要精确时间控制的非动画任务,比如定期请求数据、倒计时功能。
代码示例对比
用 requestAnimationFrame 实现动画:
let startTime = null;
const element = document.getElementById('box');
function animate(currentTime) {
if (!startTime) startTime = currentTime;
const elapsed = currentTime - startTime;
// 假设动画持续2秒
const progress = Math.min(elapsed / 2000, 1);
element.style.transform = `translateX(${progress * 200}px)`;
if (progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);用 setInterval 实现动画:
const element = document.getElementById('box');
const startTime = Date.now();
const duration = 2000; // 动画持续2秒
const intervalId = setInterval(() => {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
element.style.transform = `translateX(${progress * 200}px)`;
if (progress >= 1) {
clearInterval(intervalId);
}
}, 16); // 大约每秒60帧requestAnimationFrame 的动画更流畅,而且有浏览器优化。setInterval 的动画可能会卡顿。
实际使用建议
优先选择 requestAnimationFrame
做动画时首先考虑用它,性能和流畅度都更好。处理兼容性问题
对于不支持 requestAnimationFrame 的老浏览器,可以这样降级:window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { return setTimeout(callback, 1000 / 60); };避免在动画回调中做复杂操作
不管是 requestAnimationFrame 还是定时器,在回调函数里都不要做太复杂的计算或dom操作,否则会影响动画性能。及时清理动画
动画不需要时要用 cancelAnimationFrame 或 clearInterval 及时停止:// 对于 requestAnimationFrame cancelAnimationFrame(requestId); // 对于 setInterval clearInterval(intervalId);控制动画频率
如果不需要60帧的高频率,可以这样控制:let lastTime = 0; const fps = 30; // 每秒30帧 function animate(currentTime) { if (currentTime - lastTime > 1000 / fps) { // 执行动画逻辑 lastTime = currentTime; } requestAnimationFrame(animate); }
特殊场景考虑
游戏开发
复杂游戏通常用 requestAnimationFrame,但要注意:
不同显示器刷新率可能不同
需要处理帧率波动
可以考虑使用固定时间步长
简单UI动画
比如淡入淡出、滑动效果,直接用 css 动画可能更简单,性能也更好。
后台任务
如果是数据同步、定期检查等非动画任务,用 setInterval 更合适。
性能监控
可以监控动画性能,发现问题:
let frameCount = 0;
let lastTime = performance.now();
function checkFPS(currentTime) {
frameCount++;
if (currentTime - lastTime >= 1000) {
console.log('当前FPS:', frameCount);
frameCount = 0;
lastTime = currentTime;
}
requestAnimationFrame(checkFPS);
}
requestAnimationFrame(checkFPS);总结
requestAnimationFrame 在大多数动画场景下表现更好,是现代前端动画的首选。但定时器在特定情况下仍然有用。
选择时要考虑:
动画复杂度
性能要求
浏览器兼容性
开发成本
理解两者的区别,能帮你做出合适的选择,做出既好看又流畅的动画效果。记住,好的动画不仅要效果漂亮,还要性能优秀,不给用户设备造成负担。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!