文章目录
js实现动画
-
实现方式
requestAnimationFrame
:将执行动画的每一步传到requestAnimationFrame中,在每次执行完后进行异步回调来连续触发动画效果。setTimeout
和setInterval
+css改变(导致页面频繁性重排重绘,消耗性能)
-
优点:
- JavaScript动画控制能力很强, 可以在动画播放过程中对动画进行控制:开始、暂停、回放、终止、取消都是可以做到的。
- 动画效果比css3动画丰富,有些动画效果,比如曲线运动,冲击闪烁,视差滚动效果,只有JavaScript动画才能完成
- CSS3有兼容性问题,而JS大多时候没有兼容性问题
-
缺点:
- JavaScript在浏览器的主线程中运行,而主线程中还有其它需要运行的JavaScript脚本、样式计算、布局、绘制任务等,对其干扰导致线程可能出现阻塞,从而造成丢帧的情况。
- 代码的复杂度高于CSS动画
css实现动画
-
实现方式:
transition
:用来设置样式的属性值是如何从一种状态平滑过渡到另外一种状态,四个参数功能分别为CSS属性的名称、动画时间、速度曲线、动画何时开始transform
:2D或者3D的转换,用来设置元素的形状改变animation
:逐帧动画,通过设置帧函数,@keyframes
-
优点:
- 浏览器可以对css动画进行优化,浏览器可以对css动画进行优化,其优化原理类似于requestAnimationFrame,会把每一帧的DOM操作都集中起来,在一次重绘和回流中去完成。一般来说频率为每秒60帧。隐藏和不可见的dom不会进行重绘或回流,这样就会更少的使用CPU,GPU和内存使用量。
- 强制使用硬件加速
- 代码相对简单,性能调优方向固定
- 对于帧速不好的浏览器,css3可以做到自动降级,js则需要添加额外的代码。
-
缺点:
- 运行过程控制较弱,无法附加事件绑定回调函数。CSS动画只能暂停,不能在动画中寻找一个特定的时间点,不能在半路反转动画,不能变换时间尺度,不能在特定的位置添加回调函数或是绑定回放事件,无进度报告
- 代码冗长。想用 CSS 实现稍微复杂一点动画,最后CSS代码都会变得非常笨重。
对比两种方式和各自的适用场景
-
在需要对动画进行大量控制时,使用 JavaScript
-
如果您需要手动协调整个场景,可直接使用
requestAnimationFrame
-
当您为 UI 元素采用较小的独立状态时,使用 CSS
关于requestAnimationFrame的理解
mdn:告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。早期的动画是用setTimeout和setInterval,但是由于受浏览器的事件循环影响,时间不是那么准确(css3带来了transition
和animation
),requestAnimationFrame调用时间则是跟着系统的刷新频率走的,算是一种动画优化方式。
优势:
- CPU节能:使用SetTinterval 实现的动画,当页面被隐藏或最小化时,SetTinterval 仍然在后台执行动画任务,由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,完全是浪费CPU资源。而RequestAnimationFrame则完全不同,当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统走的RequestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。
- 函数节流:在高频率事件( resize, scroll 等)中,为了防止在一个刷新间隔内发生多次函数执行,RequestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销,一个刷新间隔内函数执行多次时没有意义的,因为多数显示器每16.7ms刷新一次,多次绘制并不会在屏幕上体现出来。
- 减少DOM操作:requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
requestAnimationFrame实现setInterval和setTimeout
function mySetInterval(fn, time) {
let timer;
const now = Date.now;
let startTime = now();
let endTime = startTime;
const loop = () => {
timer = window.requestAnimationFrame(loop);
endTime = now();
if (endTime - startTime >= time) {
startTime = endTime = now();
fn(timer)
}
}
timer = window.requestAnimationFrame(loop);
return timer;
}
function mySetTimeout(fn, time) {
let timer;
const now = Date.now;
let startTime = now();
let endTime = startTime;
const loop = () => {
timer = window.requestAnimationFrame(loop);
endTime = now();
if (endTime - startTime >= time) {
startTime = endTime = now();
fn()
window.cancelAnimationFrame(timer)
}
}
timer = window.requestAnimationFrame(loop);
return timer;
}
let a = 0;
mySetInterval((timer) => {
console.log(a++);
if (a === 3) {
window.cancelAnimationFrame(timer)
}
}, 1000)