zoukankan      html  css  js  c++  java
  • 【翻译】CSS Animations VS the Web Animations API:案例学习

    原文地址:CSS Animations vs the Web Animations API: A Case Study


    May 03, 2017 css, javascript

    上周我写了我如何使用CSS来制作bitsofcode logo动画。之后,有人建议我应该对CSS动画与Web Animations API做个对比,所以,这篇文章就是。

    Web Animations API介绍

    和上周一样,在开始之前我先对Web Animations API做下介绍。Web Animations API向开发者提供了通过JavaScript直接操作浏览器的动画引擎的方法。

    创建动画

    为了使用Web Animations API创建动画,我们需通过Element.animate()函数,该函数接受两个参数:keyframesoptions

    element.animate(keyframes, options);  
    

    关键帧 keyframes

    关键帧对象相当于动画中事件的时间轴,这里有两种方式来创建关键帧对象。为了列举这两种方式,这里以grow动画为例,该动画将会将元素的尺寸放大两倍。如下是通过CSS的@keyframes来声明:

    @keyframes grow {
        0% {
            transform: none;
        }
        100% {
            transform: scale(2);
        }
    }
    

    第一种方式

    关键帧的第一种方式是单个对象形式。对象的每个key代表了我们想要动画的CSS属性。每一个key的值都是一个由动画过程中的CSS值组成的数组。数组中的每个值表示动画时间轴中的一个点。

    const growKeyframes = {  
        transform: ['none', 'scale(2)']
    }
    

    第二种方式

    关键帧的第二种方式是写一个数组。数组中的每一个项表示动画时间轴中的一个点,在这个点上我们可以定义应用在该点上的每一条CSS属性与值。

    const growKeyframes = [  
        { transform: 'none' },
        { transform: 'scale(2)' }
    ]
    

    默认情况下,时间轴上的每个点之间的空间是相等的。例如,如果在时间轴上我有5个点,则每个点之间的动画过渡时间都会是20%。

    如果我们想自己改变过渡时间,可以使用上述中的第二种定义方式中的offset属性。该属性接受一个数字:0-1之间,表示该点动画应该在什么时候开始,例如,下面的CSS动画:

    @keyframes alteredGrow {
        0% { transform: none; }
        10% { transform: scale(1.5) }
        30% { transform: scale(1.9) }
        100% { transform: scale(2) }
    }
    

    为了说明时间轴上的不相等的空间,我可以通过下面的方式书写:

    const alteredGrowKeyframes = [  
        { transform: 'none' },
        { transform: 'scale(1.5)', offset: 0.1 }
        { transform: 'scale(1.9)', offset: 0.3 }
        { transform: 'scale(2)' }
    ]
    

    选项 options

    传递给animate()函数的第二个参数是一个带有动画选项信息的对象。如果我们使用CSS动画,这个对象允许我们指定应用于CSS动画的所有的属性。这里可以定义九个选项。

    选项 描述
    id 动画的唯一索引
    delay 定义动画开始前的延迟时间。和CSS属性animation-delay一致
    duration 定义动画一个周期需要的时间。和CSS属性animation-duration一致
    iterations 定义动画重复的次数。和CSS属性animation-iteration-count一致
    direction 定义动画在时间轴的哪个方向运行。和CSS属性animation-direction一致
    easing 定义动画每一步之间如何过渡。和CSS属性animation-timing-function一致
    fill 定义动画在播放前或后,动画效果是否可见。和CSS属性animation-fill-mode一致
    endDelay 当一个动画结束后,到下一个动画开始,中间停留的时间,毫秒
    iterationStart 定义动画的循环从哪里开始,可以是0~1,0.5表示动画从中间开始运行

    举个例子,让我们开始另一个grow动画。利用CSS动画,我们可以让动画持续3秒、无限循环、改变运行方向并且等待2秒后执行,如下是声明代码:

    .animated-element {
        animation-name: alteredGrow;
        animation-duration: 3s;
        animation-iteration-count: infinite;
        animation-direction: alternate;
        animation-delay: 2s;
    }
    

    使用Web Animations API,我们可以通过如下选项来达到相同的效果。

    const alteredGrowOptions = {  
        duration: 3000,
        iterations: Infinity,
        direction: 'alternate',
        delay: 2000
    }
    

    使用动画

    通过元素的调用的animate()函数可以将动画应用到该元素上,并且将关键帧(keyframes)选项(options)作为参数。

    const element = document.querySelector('.animated-element');  
    element.animate(alteredGrowKeyframes, alteredGrowOptions);  
    

    一旦函数被调用,动画就会自动的开始运行。然而,我们可以通过play()pause()方法来开始与暂停动画。

    const element = document.querySelector('.animated-element');  
    const myAnimation = element.animate(alteredGrowKeyframes, alteredGrowOptions);
    
    myAnimation.pause();  
    myAnimation.play();  
    

    兼容性

    和我的CSS动画一样,我从创建好的完整的动画中截取了一小段。下面是三个版本的对比:

    animate

    创建时间轴

    概括来说,如下是logo左侧部分的动画步骤(字母“bitso”中的o是开的,类似()。

    • 向左移动
    • 回到中间
    • 停留在中间(等待右边部分向右移动)
    • 向左移动
    • 旋转
    • 慢慢增加旋转
    • 回到没有旋转的位置
    • 回到中间

    基于上述步骤,这是我制作的左边部分的时间轴

    Timeline

    下面是基于该时间轴利用Web Animation API创建的关键帧对象,具有时间轴上的每一步的样式规则。

    const logoSectionLeftKeyframes = [  
      { transform: 'none' },
      { offset: 0.125, transform: 'translateX(-15px)' },
      { offset: 0.25, transform: 'none' },
      { offset: 0.5, transform: 'none' },
      { offset: 0.625, transform: 'translateX(-15px)' },
      { offset: 0.67, transform: 'translateX(-15px) rotate(-10deg)' },
      { offset: 0.72, transform: 'translateX(-15px) rotate(-10deg)' },
      { offset: 0.82, transform: 'translateX(-15px) rotate(-15deg)' },
      { offset: 0.875, transform: 'translateX(-15px)' },
      { transform: 'none' }
    ];
    

    因为用到了offset属性,所以这里我决定使用数组形式的keyframes

    设置参数

    每一部分的参数都很简单,每个循环周期时长3秒,并且无限循环。

    const logoSectionOptions = {  
      duration: 3000,
      iterations: Infinity
    };
    

    应用动画

    使用Web Animation API来应用动画要比CSS动画有些繁琐。这是因为我只想当动画被hover或者focus的时候来运行。正如我所说的,默认情况下,Web Animation会在创建完之后立即运行。

    为了解决这个问题,我不得不创建完动画后立即暂停,然后增加事件监听器,当事件触发,动画就可以播放或者暂停。另外,因为我要应用动画到每个字符上,那就必须要同时处理几个动画,下面是执行过程:

    // 存储所有动画的数组
    const animations = [];
    
    function playLogoAnimation() {  
      animations.map((animation) => animation.play())
    }
    
    function pauseLogoAnimation() {  
      animations.map((animation) => {
          animation.pause();
          animation.currentTime = 0; // 重置动画为初始状态
      })
    }
    
    function createLogoAnimation() {  
      const logoSectionLeftEls = Array.from( document.querySelectorAll('.logo-section-left') );
      logoSectionLeftEls.forEach((el) => animations.push(el.animate(logoSectionLeftKeyframes, logoSectionTiming)))
    
      // Animation for middle and right sections here …
    
      // 一旦创建完动画就立即停止
      pauseLogoAnimation();
    }
    
    createLogoAnimation();
    
    // 时间监听器来开始或暂停动画
    const siteTitleLink = document.querySelector('.site__title a');  
    siteTitleLink.addEventListener('mouseover', playLogoAnimation);  
    siteTitleLink.addEventListener('mouseout', pauseLogoAnimation);  
    siteTitleLink.addEventListener('keyup', (e) => {  
      if ( e.keyCode === 9 ) playLogoAnimation();
    });
    siteTitleLink.addEventListener('keydown', (e) => {  
      if ( e.keyCode === 9 ) pauseLogoAnimation();
    });
    

    下面是全部动画演示:

    CSS Animations VS the Web Animation API

    和其他一切一样,无论使用CSS还是JavaScript动画,取决于动画的细节。作为基本规则,CSS动画应该用在小型、和UI相关的动画,比如提示框(tooltip)。Web Animation API应该用作需要微调的更高级的效果。这就是我对这两种动画方式的比价。

    性能

    CSS vs JavaScript动画的性能会根据我们正在动画的属性而有很大的不同。通常,建议只对transform或者opacity属性进行动画,因为这些动画会在与浏览器主线程不同的线程上执行。

    改变transform并不会触发任何几何形状的改变或渲染,性能会很好。这意味着操作可能在GPU的帮助下由合成器线程执行。
    -- CSS Triggers

    由于我的动画只用了transform属性,所以我不能看到这两种方案之间明显的性能差异。通过火狐的开发者工具,我测量了两个动画的帧率,获得了两个相同的帧率:60FPS,即使Main Thread Animation enabled为Off。

    我无法找到更多的方式去测量两种方案的性能。如果你知道其他更好的方法,可以在下面留下评论。

    开发者经验

    在这个例子中,我个人发现CSS动画要比Web Animation API操作简单,更容易使用,主要是因为使用后者来播放暂停动画需要额外的工作。如果我是在做一个更复杂的动画比如一个游戏,Web Animation API会是一个一定要走的路。但是在这个例子中,我认为使用CSS动画更容易实现。

  • 相关阅读:
    ActiveMQ (二):JMS
    Java消息队列--ActiveMq 初体验
    利用 UltraEdit 重新排版 XML 结构数据
    Java中的Arrays工具类
    数组的下标与长度
    数组的一维与多维
    MySQL数据库的下载与安装
    MySQL数据库的发展历程
    Java中的数组(Array)
    break与continue关键字
  • 原文地址:https://www.cnblogs.com/yangyoucun/p/6849918.html
Copyright © 2011-2022 走看看