header-bg.jpg
Vue 中使用 requestAnimationFrame 制作动画
发表于 2019-04-25 20:41
|
分类于 JavaScript
|
评论次数 0
|
阅读次数 1683

requestAnimationFrame 是浏览器用于定时循环操作的一个接口,类似于 setTimeout,主要用途是按帧对网页进行重绘。

设置这个 API 的目的是为了让各种网页动画效果(DOM 动画、Canvas 动画、SVG 动画、WebGL 动画)能够有一个统一的刷新机制。

它采用系统时间间隔, 保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销。也不会因为间隔时间太长,使用动画卡顿不流畅,从而节省系统资源,提高系统性能,改善视觉效果。

代码中使用这个 API,就是告诉浏览器希望执行一个动画,让浏览器在下一个动画帧安排一次网页重绘。

特点

requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率

在隐藏或不可见的元素中,requestAnimationFrame 将不会进行重绘或回流,这当然就意味着更少的 CPU、GPU 和内存使用量

requestAnimationFrame 是由浏览器专门为动画提供的 API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了 CPU 开销

在 Vue 中使用

举个例子, 例如我想实现如下的动画效果:

trigger.gif

实现上图的效果, 需要如下一张原始背景图片:

themetrigger.png

通过不断切换背景图片的定位, 从而达到一张纸被掀开的效果。

此时 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 停止动画。

网上的文章一般都是像第二种这样使用, 但是我比较喜欢第一种方式, 嘤嘤嘤~

发布评论
还没有评论,快来抢沙发吧!