如何调试CSS的关键帧(keyframe)动画

如何调试CSS的关键帧(keyframe)动画

我们可以学习语法来创建CSS动画,但创造美丽流畅的动画可能就要关注更多细节了。如今动画被高度关注,因此正确调整代码动画的时间,以及出错时各种调试方法突显重要。在自己解决这个问题后整理一番。

使用负值延迟

当多个动画同时运行,我们想他们错开一点。这时可以用animation-delay,但你不一定想用户访问页面时,他们要等待延迟看到某些元素不动的情况。

这时,将animation-delay设置成一个负数,它就会跳过几秒直接进入动画,因此显示器显示时所有的动画都是运行状态。当动画有相同的的关键帧动画(keyframe values)而只是运动延迟时间不同,这个方法就可以大展身手了。

当然,使用该概念进行调试也是不错的选择。设置animation-play-state; 然后调整不同负时间的延迟。你会看到动画在运动中的不同暂停状态。

1
2
3
4
5
.thing {
animation: move 2s linear infinite alternate;
animation-play-state: paused;
animation-delay: -1s;
}

例:

在下面这个有趣的演示中,我们让两个机器人在一个稍微不同的时间移动,从而让动画更自然。这里也是给紫机器人使用负延迟来运行动画,这样用户看到页面时他已经在运动了。

多个transform

为了获得最好的性能,您还应该使用transform来进行移动和变换,从而节省重绘margin / top / left 之类的成本。 Paul Lewis做了一个CSS Triggers的列表来表示这些消耗。如果你想移动的东西有多种变换,那么也会有一些问题对应而来。

其中一个大问题就是他们的顺序。transform不会像人们预计的那样同时运行,而是像订单一样操作(有一定的先后顺序)。先运行的是最右边最远的一个,然后向内。例如,在下面的代码里,缩放会先执行(scale),然后变换(translate),最后旋转(rotate)。

1
2
3
4
5
6
keyframes foo {
to {
/* 第三 第二 第一 */
transform: rotate(90deg) translateX(30px) scale(1.5);
}
}

然而在大多数情况下这并不是理想情况,有时候你宁愿他们同时运动。此外,当你开始在多个keyframe里同时使用transform的值时候情况会变得更加复杂,像这样:

1
2
3
4
5
6
7
8
9
10
11
@keyframes foo {
30% {
transform: rotateY(360deg);
}

65% {
transform: translateY(-30px) rotateY(-360deg) scale(1.5);
}

90% {
transform: translateY(10px) scale(0.75);
}

}

然后就有一些奇怪的,出乎意料的事情发生了。不幸的是,解决这个问题的答案通常是嵌套多个< div >,每个用一种translation,这样才不会出现冲突。
像这样:


当然也有一些解决方法,比如使用矩阵变换(不是直观的手工代码)或使用一个JavaScript动画API,如GreenSock,它可以让你不需要使用有序的序列来插入多个变换。

多使用个div也可以解决SVG的一些问题。例如在safari中,你不能在一个动画中同时声明透明度(opacity)和转换(transform),不然其中一个肯定无效。
2015年8月初,独立的变换声明(ndependent transform declarations)已经加入到了Chrome的Canary版本中。这意味着我们不用担心用更多的变换效果,可以分别声明rotate,translatescale

## 使用DevTools的时间控制器
无论是chrome还是firefox现在都有一些专门用于调试动画的工具,比如可以控制动画速度的滑块,暂停按钮和一些简单数值的UI测试(UI for working with the easing values),通过放慢速度,暂停来观察动画的特殊点,是CSS动画中的一个非常有用的调试方法。

两个浏览器都使用Lea Verou的cubic-bezier.com的可视化GUI。因此我们就不用反复去cubic-bezier.com调整再修改文本编辑器中的代码了。

这些工具让我们更直观的调整动画,来看看两个浏览器的界面:
chrome animation tool

firefox animation tool

chrome和firefox不仅可以控制动画时间(加速或者减速),还可以使用自定义动画。更为先进的时间轴工具也即将chrome发布( youtube视频需翻墙),它可以同时观看多个元素。毕竟,一次只能处理一个元素实在是动画调整中一个不小的限制。

还有一个麻烦就是如果动画的存在时间很短很快我就很难抓取到了,所以通常要对其设置animation-iteration-count: infinite;,才能不用太在意时间继续调试。

当然还有一些额外的工具去帮助我们让动画在浏览器中慢下来以此调整,回放。它允许你从根本上分开每个动作,观察元素之间的相互影响以及他们的运动状态。当你改变它的速度使其变流畅,整体效果也会更加出色的。

## 使用JavaScript调试CSS动画事件
如果你不想去计算动画到底是什么时间什么地方出错了,你可以在事件触发时用一点点的javascript来调试,将其添加到animationstartanimationiterationanimationend事件上。
例如:

保持keyframe整洁

我经常看到有些人在keyframe的0%和100%中声明相同的属性值,这是完全没必要的而且会让代码变得很臃肿。浏览器将默认属性值作为初始值和结束值。

像这样子就是太肿了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.element {
animation: animation-name 2s linear infinite;
}


@keyframes animation-name {
0% {
transform: translateX(200px);
}

50% {
transform: translateX(350px);
}

100% {
transform: translateX(200px);
}

}

正确的写法:

1
2
3
4
5
6
7
8
9
10
.element {
transform: translateX(200px);
animation: animation-name 2s linear infinite;
}


@keyframes animation-name {
50% {
transform: translateX(350px);
}

}

DRY-ing Out Animations

创造简洁漂亮的动画通常意味着写一个具体的cubic-bezier()函数。调整Easing函数的工作类似于公司的调色板。你可以在运动中拥有属于自己个性的标志和“声音”。如果你想在整个网站中使用(为了保持统一,你应该这样做),最简单的方法是将一个或两个Easing函数存储在一个变量中,就像我们将颜料混合在调色板里。使用SASS或者其他的于处理器可以让其更简单:

1
2
3
4
5
6
//scss
$smooth: cubic-bezier(0.17, 0.67, 0.48, 1.28);

.foo { animation: animation-name 3s $smooth; }

.bar { animation: animation-name 1s $smooth; }

当使用css的keyframe来做动画时我们希望尽可能用到GPU的性能,这就意味着如果你有多个动画你想更容易的DOM操作给接下来要加载的运动和懒加载的元素。你可以用CSS中的标准声明块让硬件加速原生的DOM(不是SVG)。复用元素的动画是有意义的,这里使用mixin:

1
2
3
4
5
6
7
8
9
10
@mixin accelerate($name) {
will-change: $name;
transform: translateZ(0);
backface-visibility: hidden;
perspective: 1000px;
}

.foo {
@include accelerate(transform);
}

当心,一次性转移太多元素可能会造成相反的效果并影响其性能。大多数动画应该正常,但是要如果你使用类似haml之类的标记语言来生成大量DOM元素时候要注意。

使用循环来获得更好的性能

Smashing Magazine最近发表的一个工作之余的神奇项目,http://species-in-pieces.com/。在一个特定的部分,作者讨论了关于动画细节,其碎片导致的性能问题。他说:

假设你正在同时移动三十个物体,浏览器就要处理很多东西,然后或许就会有一些问题出现。如果你每个元素有0,199秒的速度和0.2秒的延迟,你可能会一次移动一个元素来修复这些问题。事实上,相同的运动并没必要重复产生。如果动画是一个链,性能是立即提高了30倍。

1
2
3
4
5
@for $i from 1 through $n {
&:nth-child(#{$i}) {
animation: loadIn 2s #{$i*0.11}s $easeOutSine forwards;
}
}

不仅如此,您可以使用它们来产生像颜色交错的视觉效果。(点击回放重新运行动画。)

调整多动画序列

当你在创建长动画时,我们通常将其排列成链式。类似:

1
animation: foo 3s ease-in-out, bar 4s 3s ease-in-out, brainz 6s 7s ease-in-out;

但是我们想想当你优化的时候,你发现你想改变动画的第二个属性值,这会影响接下来的它之后的所有元素,所以当我们调整之后。好像没什么大问题:

1
animation: foo 2.5s ease-in-out, bar 4s 2.5s ease-in-out, brainz 6s 6.5s ease-in-out;

现在让我们添加另一个动画也要再次进行调整(好的动画产生之前这种微调似乎是必然的),于是事情的效率好像就有点低了。当你这么做了3次,真的就非常低效率。
接着你可以想象一下如果你有两个动画并且要让他们保持一致…嗯你理解就好。这就是为什么每当当链式动画效果超越三个或四个,我就会使用JavaScript了。就个人而言,我比较喜欢GreenSock animation API因为它有非常强健的时间轴功能,但是很多JS允许你在不重新计算的情况下使用简单的堆栈动画创造工作流。

制作一个动画不仅仅是构建起来。通常情况下,它是人们通过编辑精炼和调试从而让一个项目从一个只是精心策划过的的运动变成一个有更好的性能的动画。希望这些建议能够成为你工具箱的工具,使你工作起来更加顺畅。


本文根据@SARAH DRASNER的文章所译,整篇译文带有自己的理解和意思,如果有译得不好的地方或者不对之处,还请大家指点。英文出处:Debugging CSS Keyframe Animations