参考文章
https://mayandev.top/2020/03/18/interview-question-archive/
https://www.cnblogs.com/onepixel/p/7078617.html
实现动画的方式
- JavaScript: setTimeout
- CSS3: transform、animation
- Html5: canvas、requestAnimationFrame(请求动画帧)
setTimeout动画出现卡顿、抖动的原因
- setTimeout属于异步任务,需要等主线程空闲才能执行,所以实际执行时间一般比设定的要晚一些 [ JS事件循环 ]
- 不同设备的屏幕刷新率不同,setTimeout只能设置一个固定的时间间隔,这个时间不一定和屏幕的刷新时间相同
动画优化
- 优先采用requestAnimationFrame,实现动画帧的并发渲染
- 做减法,保留主动画性能,去除重要性不大的动画(跑马灯、过程小动画)
- 大图动画性能消耗非常大,使用transform3D实现GPU加速,动画结束、暂停时,切换回2D,取消加速
- 按需加载 / 卸载动画
- 动画帧处理函数简化,减少或去除回流与重绘
requestAnimationFrame
与setTimeout相比,requestAnimationFrame最大的优势:
- 由系统决定回调函数的执行时间
- 在屏幕刷新前调用回调函数
- 保证回调函数在每次屏幕刷新间隔中只执行一次
let progress = 0
function render() {
progress += 1;
if (progress < 100) {
window.requestAnimationFrame(render);
}
}
// 第一帧渲染
window.requestAnimationFrame(render);
其他优势
-
CPU节能
- [×] setTimeout动画,在页面隐藏或最小化后,仍在后台执行动画
- [√] requestAnimationFrame,页面未激活状态下暂停动画,页面激活后从上次停止的地方继续执行
-
函数节流
- [√] 高频事件(onresize等),防止一个刷新间隔内多次执行,没有意义,多次绘制屏幕体现不出来
requestAnimationFrame保证刷新间隔内,函数只执行一次
- [√] 高频事件(onresize等),防止一个刷新间隔内多次执行,没有意义,多次绘制屏幕体现不出来
优雅降级 - polyfill
requestAnimation存在兼容问题,不同浏览器还需要不同前缀
if(!Date.now)
Date.now = function() { return new Date().getTime(); };
(function() {
'use strict';
var vendors = ['webkit', 'moz'];
for(var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
var vp = vendors[i];
window.requestAnimationFrame = window[vp+'RequestAnimationFrame'];
window.cancelAnimationFrame = (
window[vp+'CancelAnimationFrame'] ||
window[vp+'CancelRequestAnimationFrame']
);
}
if(
/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) || // iOS6 is buggy
!window.requestAnimationFrame ||
!window.cancelAnimationFrame
) {
var lastTime = 0;
window.requestAnimationFrame = function(callback) {
var now = Date.now();
var nextTime = Math.max(lastTime + 16, now);
return setTimeout(function() {
callback(lastTime = nextTime)
},nextTime - now);
};
window.cancelAnimationFrame = clearTimeout;
}
}());