DIY Web Animations: Promise + rAF + Transition

发布于 大漠

Web Animation API非常的棒。除了能很好的支持平常动画之外。我发现还可以使用Promise、rAF和CSS Transition来重新创建它们,结果类似人体工程学。

Web Animations

使用Web动画,你可以让任何元素,并让它按动画序列帧播放

element.animate([
    {transform: 'translateX(0px)', backgroundColor: 'red'},
    {transform: 'translateX(100px)', backgroundColor: 'blue'},
    {transform: 'translateX(50px)', backgroundColor: 'green'},
    {transform: 'translateX(0px)', backgroundColor: 'red'},
    //...
], {
    duration: 3000,
    iterations: 3,
    delay: 0
}).finish.then(_ => console.log('I’m done animating!'));

很完美,对吧?而这只是涉及Web动画的冰山一角。该规范有更多的功能,如动画的合成,处理多个时间轴和事件处理。如果你想了解更多,可以查阅动画相关规范,但遗憾的是,现在只有Firefox和Chrome浏览器支持,而且仅支持部分功能。如果你想在生产中使用这个,那还是需要做一定的考虑。

DIY

然而,对于日常开发而言,要真正的像上面那样使用:用动画系列声明的方式来定义动画。需要使用Web Animations Polyfill,它包含了我们实际所需要的更多功能。

Object.assign(element.style,
    {
        transition: 'transform 1s, background-color 1s',
        backgroundColor: 'red',
        transform: 'translateX(0px)',
    }
);

requestAnimationFramePromise()
    .then(_ => animate(element,
        {transform: 'translateX(100px)', backgroundColor: 'blue'}))
    .then(_ => animate(element,
        {transform: 'translateX(50px)', backgroundColor: 'green'}))
    .then(_ => animate(element,
        {transform: 'translateX(0px)', backgroundColor: 'red'}))
    .then(_ => console.log('I’m done animating!'));

不是很好,但是在所有的浏览器中都可以工作,而且还能很好的工作,你不觉得吗?最奇怪的是,你可能需要在第一个关键帧上的定义和其他关键帧不同。如果你能使用ES2017的async/await,那么你可以使用更少的缩进。

我是怎么实现的呢?如果你用JavaScript来讨论连锁(Chains),你必然会想到Promises。这也是我为什么要对requestAnimationFrame和CSS Transition进行包装的原因。也许这可能会让人感到惊讶,但这的确是你所需要的。

CSS Transition包装

当你使用CSS Transition让一个元素具有一个动画效果,其实他有一个transitioned事件:

function transitionEndPromise(element) {
    return new Promise(resolve => {
        element.addEventListener('transitionend', function f() {
        element.removeEventListener('transitionend', f);
        resolve();
        });
    });
}

这样使用后不会注册我们的侦听器,也不会泄露内存!有了这个,我可以使用Promises替代回调,等待动画的结束。

rAF包装

我们也可以对requestAnimationFrame进行包装,让其变得更简单:

function requestAnimationFramePromise() {
    return new Promise(resolve => requestAnimationFrame(resolve));
}

有了这个,我们可以使用Promise而不是回调来等待下一帧。

我们自己的animate()

我们可以将它合并到我们自己的element.animate()中。

function animate(element, stylz) {
    Object.assign(element.style, stylz);
    return transitionEndPromise(element)
        .then(_ => requestAnimationFramePromise());
}

这就是它!这就是背后执行的所有事情,让我们的代码能更好的工作。这是对animationtransition非常轻量的抽象处理,会给很多开发人员带来很多的便利。不要忘了Promise.all()这样的Promise工具,它可以将多个动画合并在一起执行。这个概念可以很容易地应用到JavaScript生态系统中的构造器中。

Trip wires

显然,我需要提醒自己,事件冒泡

这意味着,如果您在两个元素上使用这个技术,而其中一个元素是另一个元素的祖先,那么从继承者的传递结束事件将使前面的动画链向前推进。幸运的是,通过检查事件,可以很容易的解决这样的现象,比如event.target

function transitionEndPromise(element) {
    return new Promise(resolve => {
        element.addEventListener('transitionend', function f(event) {
        if (event.target !== element) return;
        element.removeEventListener('transitionend', f);
        resolve();
        });
    });
}

本文根据@Surma的《DIY Web Animations: Promises + rAF + Transitions》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://dassur.ma/things/raf-promise/

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:https://www.fedev.cn/animations/raf-promise.htmlNike Mercurial Superfly V FG Spark Brilliance Elite Pack ACC Soccers Grey Blue Orange