css 动画和 Web Animation API(WAAPI)对比
有一个JavaScript的动画原生API,称为Web Animation API
。我们在这篇文章中称之为WAAPI
。MDN有很好的文档,Dan Wilson 有一个伟大的系列文章。
关于浏览器支持的提示
尽管现在浏览器的支持很有限,但WAAPI有一个全面而强大的polyfill工具,让它可以在当前的生产环境中使用。
一如既往,您可以检查Can I Use 的浏览器支持数据。然而,这并没有提供非常好的信息来支持WAAPI的所有子功能。这是一个检查器:
想要不使用polyfill而体验所有功能,请使用Firefox Nightly。
WAAPI的基础知识
如果您曾经使用jQuery.animate()
,WAAPI的基本语法应该看起来很熟悉。
var element = document.querySelector('.animate-me');
element.animate(keyframes, 1000);
该animate
方法接受两个参数:关键帧和持续时间。与jQuery不同的是,它不仅具有内置在浏览器中的优点,而且性能也更高。
第一个参数,关键帧应该是一个对象数组。每个对象都是我们动画中的一个关键帧。这是一个简单的例子:
var keyframes = [
{ opacity: 0 },
{ opacity: 1 }
];
第二个参数,持续时间,是我们想要动画持续多久。在上面的例子中是1000毫秒。我们来看一个更令人兴奋的例子。
用WAAPI重新创建animista CSS动画
这里有一些CSS代码,从非常棒的animista中吸取了一些称为“幻灯片模糊的”入场动画的东西。看起来很可爱。
以下是CSS中的关键帧:
0% {
transform: translateY(-1000px) scaleY(2.5) scaleX(.2);
transform-origin: 50% 0;
filter: blur(40px);
opacity: 0;
}
100% {
transform: translateY(0) scaleY(1) scaleX(1);
transform-origin: 50% 50%;
filter: blur(0);
opacity: 1;
}
WAAPI中的代码相同:
var keyframes = [
{
transform: 'translateY(-1000px) scaleY(2.5) scaleX(.2)',
transformOrigin: '50% 0', filter: 'blur(40px)', opacity: 0
},
{
transform: 'translateY(0) scaleY(1) scaleX(1)',
transformOrigin: '50% 50%',
filter: 'blur(0)',
opacity: 1
}
];
我们已经看到,将关键帧应用到要运动的任何元素是多么容易的事:
element.animate(keyframes, 700);
为了简单起见,我只指定了持续时间。但是,我们可以使用这个第二个参数来传递更多的选项。至少我们也应该指定一个缓动函数。以下是可用选项的完整列表,其中包含一些示例值:
var options = {
iterations: Infinity,
iterationStart: 0,
delay: 0,
endDelay: 0,
direction: 'alternate',
duration: 700,
fill: 'forwards',
easing: 'ease-out',
}
element.animate(keyframes, options);
有了这些选项,我们的动画将从头开始,没有任何延迟,永远循环在向前和向后播放。
令人烦恼的是,对于我们熟悉CSS动画的人来说,一些术语与我们习惯的不同。虽然在积极的一面看,有些事情写起来更快一点!
- 这里用
easing
而不是animation-timing-function
- 使用
iterations
而不是animation-iteration-count
。如果我们希望动画永远重复,用Infinity
而不是infinite
。有点混乱,Infinity
不是引用。Infinity是一个JavaScript关键字,而其他值是字符串。 - 我们使用毫秒而不是秒,对于之前编写过许多JavaScript的人来说,这应该是熟悉的。(您可以在CSS动画中使用毫秒数,但很少有人使用。)
我们来仔细看看一个选项:iterationStart
。
当我第一次碰到iterationStart
, 我被难住了。你为什么要指定迭代的开始,而不仅仅是减少迭代次数?当您使用十进制数时,此选项非常有用。例如,您可以将其设置为 .5
,动画将开始一半。要做一整个动画需要两半,所以如果你的迭代次数设置为1,并且你的iterationStart
设置为 .5
,动画将从一半到动画结束播放,然后从动画开头开始,结束于中间!
值得注意的是,您也可以将迭代次数设置为小于1。例如:
var option = {
iterations: .5,
iterationStart: .5
}
这将从中间到最后播放动画。
endDelay:endDelay
如果要将多个动画串在一起,但是希望在一个动画的结尾和任何后续动画的开始之间存在差距。这是一个有用的视频,Patrick Brosset 的解释。
缓动
缓动是任何动画中最重要的元素之一。WAAPI为我们提供了两种不同的方式设置缓动函数 - 在我们的关键帧阵列或我们的选项对象内。
在CSS中,如果你应用了,animation-timing-function: ease-in-out
你可能会假设你的动画的开始会ease in
,动画的结束将会ease out
。实际上,这些缓动函数应用在关键帧之间,而不是整个动画。这可以对动画的感觉进行细粒度的控制。WAAPI还提供这种能力。
var keyframes = [
{ opacity: 0, easing: 'ease-in' },
{ opacity: 0.5, easing: 'ease-out' },
{ opacity: 1 }
]
值得注意的是,在CSS和WAAPI中,您不应该传入最后一帧的缓动值,因为这将不起作用。这是很多人犯的错误。 有时候,在整个动画中添加缓动效果更为直观。这在CSS是不可能的,但现在可以实现与WAAPI。
var options = {
duration: 1000,
easing: 'ease-in-out',
}
你可以看到这两种缓动在CodePen上的区别:
ease vs linear
值得注意的是CSS动画和WAAPI之间的另一个区别:CSS ease的默认值是ease
,WAAPI的默认值linear
。 Ease
实际上是一个版本,ease-in-out
是一个非常好的选择,如果你感到懒惰。同时,linear
是沉闷的和无生机的 - 一致的速度看起来机械和不自然。它被选为默认值,可能因为它是最中立的选项。然而,在WAAPI中使用时,使用缓动比在CSS更重要,以免您的动画看起来很乏味和机械地。
性能
WAAPI提供与CSS动画相同的性能改进,尽管这并不意味着平滑的动画是不可避免的。
我希望这个API的性能优化意味着我们可以避免使用will-change
和完全hack translateZ
- 最终可能。但是,至少在目前的浏览器实现中,这些属性在处理闪烁问题(jank issue
)方面仍然是有帮助和必要的。
但是,至少如果您的动画有延迟,则无需担心使用will-change
。网络动画规范的主要作者对“Animation for Work Slack community”提出了一些有趣的建议,希望他不介意我在这里重复:
如果您有一个积极的延迟,您不需要
will-change
因为浏览器将在延迟开始时进行分层,当动画启动时,它将准备就绪。
WAAPI对战CSS动画?
WAAPI为我们提供了一个在JavaScript中实现css中的语法。然而,他们不应该被视为对手。如果我们决定坚持使用CSS进行animations
和transitions
,那么我们可以与WAAPI进行动画交互。
Animation 对象
该.animate()
方法不仅仅是动画我们的元素,它也返回一些东西。
如果我们看看控制台中的返回值,我们将看到它的一个动画对象。这为我们提供了各种各样的功能,其中一些是非常不言自明的myAnimation.pause()
。通过更改animation-play-state
属性,我们可以通过CSS动画实现类似的结果,但WAAPI语法比稍微简单element.style.animationPlayState = "paused"
。我们也有权力轻松地扭转我们的动画myAnimation.reverse()
,再次,animation-direction
与使用我们的脚本更改CSS属性相比,稍微有点改进。
然而,到目前为止,@keyframe
用JavaScript进行操作并不是世界上最简单的事情。即使像重新启动动画一样简单,就像Chris Coyier 先前写过的那样,这个技巧也有一些技巧。使用WAAPI,我们可以简单地使用myAnimation.play()
,它从一开始就重播动画,如果它已经完成,或者如果我们暂停播放,则从中间迭代继续播放动画。
我们甚至可以轻松地改变动画的速度。
myAnimation.playbackRate = 2; // speed it up
myAnimation.playbackRate = .4; // use a number less than one to slow it down
getAnimations()
此方法将返回任何动画对象的数组,以便我们使用WAAPI定义的任何动画以及 任何CSS transitions
或animations
。
element.getAnimations() // returns any animations or transitions applied to our element using CSS or WAAPI
如果您感觉使用CSS来定义和应用您的动画比较顺手,getAnimations()
可以使用API与@keyframes
结合使用。您可以继续使用CSS进行大部分动画工作,并在需要API时获得API的优势。让我们看看这是多么简单。
即使一个DOM元素只有一个动画应用到它,getAnimations()
也将始终返回一个数组。我们来抓住那个单一的动画对象来处理。
var h2 = document.querySelector("h2");
var myCSSAnimation = h2.getAnimations()[0];
现在我们可以在我们的CSS动画中使用web animation API :)
myCSSAnimation.playbackRate = 4;
myCSSAnimation.reverse();
Promise和事件
我们已经有多种通过CSS触发的事件,我们可以在我们的JavaScript代码利用的有:animationstart
,animationend
,animationiteration
和transitionend
。我经常需要收听animations
或transitions
的结束,以便从DOM中删除应用于它的元素。
在WAAPI 中使用animationend
或transitionend
为此目的相当于再次使用动画对象:
myAnimation.onfinish = function() {
element.remove();
}
WAAPI为我们提供了事件和Promise的选择。我们的动画的.finished
属性对象将在动画结束时返回一个resolve 的 Promise。以下是上面的例子,就像使用Promise一样:
myAnimation.finished.then(() =>
element.remove())
我们来看看Mozilla开发人员网络中的一个稍微有点省略的例子。Promise.all
接收一个Promise数组,一旦所有这些Promise得到解决,才会运行我们的回调函数。我们已经看到,element.getAnimations()
返回一个动画对象数组。我们可以将数组中的所有动画对象映射到每个动画对象.finished
上,为我们提供所需的Promise数组。
在这个例子中,只有在页面上的所有动画完成后,我们的函数才能运行。
Promise.all(document.getAnimations().map(animation =>
animation.finished)).then(function() {
// do something cool
});
未来
本文中提到的功能只是开始。目前的规范和实践看起来是一件伟大的事情的开始。
「翻譯原文」:https://css-tricks.com/css-animations-vs-web-animations-api/?utm_source=frontendfocus