requestAnimationFrame
是浏览器用于定时循环操作的一个接口,类似于 setTimeout
,主要用途是按帧对网页进行重绘。
设置这个 API 的目的是为了让各种网页动画效果(DOM 动画、Canvas 动画、SVG 动画、WebGL 动画)能够有一个统一的刷新机制。
它采用系统时间间隔, 保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销。也不会因为间隔时间太长,使用动画卡顿不流畅,从而节省系统资源,提高系统性能,改善视觉效果。
代码中使用这个 API,就是告诉浏览器希望执行一个动画,让浏览器在下一个动画帧安排一次网页重绘。
特点
requestAnimationFrame
会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
在隐藏或不可见的元素中,requestAnimationFrame
将不会进行重绘或回流,这当然就意味着更少的 CPU、GPU 和内存使用量
requestAnimationFrame
是由浏览器专门为动画提供的 API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了 CPU 开销
在 Vue 中使用
举个例子, 例如我想实现如下的动画效果:
实现上图的效果, 需要如下一张原始背景图片:
通过不断切换背景图片的定位, 从而达到一张纸被掀开的效果。
此时 requestAnimationFrame
就派上用场了:
template:
<div
@mouseenter="enter"
@mouseleave="leave"
title="更换皮肤"
class="theme-trigger"
:style="`background-position: -${position}px 0`">
</div>
css
.theme-trigger {
background-image: url(trigger.png);
background-position: 0 0;
border-radius: 0 4px 0 0;
cursor: pointer;
width: 58px;
height: 49px;
position: absolute;
top: 0;
right: 0;
}
第一种实现方式:
export default {
data() {
return {
position: 0,
is_enter: false,
}
},
methods: {
async enter() {
this.is_enter = true;
while (this.position !== 522 && this.is_enter) {
await new Promise(resolve => window.requestAnimationFrame(() => resolve(this.position += 58)));
}
},
async leave() {
this.is_enter = false;
while (this.position !== 0 && !this.is_enter) {
await new Promise(resolve => window.requestAnimationFrame(() => resolve(this.position -= 58)));
}
},
}
利用 ES7 的 async/await
特性, 在 while
中不断调用 requestAnimationFrame
, 设置定位即可
第二种实现方式:
export default {
data() {
return {
position: 0,
timer: 0,
}
},
methods: {
enlarge() {
if (this.position === 522) {
window.cancelAnimationFrame(this.timer);
} else {
this.position += 58;
this.timer = window.requestAnimationFrame(this.enlarge);
}
},
narrow() {
if (this.position === 0) {
window.cancelAnimationFrame(this.timer);
} else {
this.position -= 58;
this.timer = window.requestAnimationFrame(this.narrow);
}
},
enter() {
window.cancelAnimationFrame(this.timer);
this.timer = window.requestAnimationFrame(this.enlarge);
},
leave() {
window.cancelAnimationFrame(this.timer);
this.timer = window.requestAnimationFrame(this.narrow);
},
},
}
常规的方式, 利用了 cancelAnimationFrame
停止动画。
网上的文章一般都是像第二种这样使用, 但是我比较喜欢第一种方式, 嘤嘤嘤~
