前端开发者学堂 - fedev.cn

CSS动画概述

发布于 彦子

给HTML中的内容添加动画的三种方法之一是,CSS animation【另外两种方法是:CSS transitions及JavaScript】。CSS动画相对简单。它允许你使用元素的CSS属性来添加动画。它可以让你创造出很多非常酷的效果,如移动、淡入淡出、改变颜色,等等。

首先,我们来看看一个例子。下面是一个CSS动画,应用于云朵上,使得它们可以轻轻地上下弹跳:

本教程中,你将学习关于CSS动画内容,而不仅是创建一些类似移动云朵的东西,还包括一些更酷且实用的东西。你将学习如何实用animation属性定义一个CSS动画,如何使用keyframes,以及如何调整各种动画相关的属性,来完成你想要的动画效果。

开始!

创建一个简单的动画

学习CSS动画最简单(也最有趣)的方式是实践,然后再学习原理。先创建一个新的HTML文件,然后添加如下的HTML和CSS。

<!DOCTYPE html>
<html lang="en-us">
 
<head>
<meta charset="utf-8">
<title>Bouncing Clouds</title>
<script src="//www.kirupa.com/js/prefixfree.min.js"></script>
 
<style>
#mainContent {
    background-color: #A2BFCE;
    border-radius: 4px;
    padding: 10px;
    width: 600px;
    height: 300px;
    overflow: hidden;
}
.cloud {
    position: absolute;
}
#bigcloud {
    margin-left: 100px;
    margin-top: 15px;
}
</style>
</head>
 
<body>
    <div id="mainContent">
        <img id="bigcloud" alt="#" class="cloud" height="154" src="//www.kirupa.com/images/bigCloud.png" width="238">
    </div>
</body>
 
</html>

预览一下,你会看到如下的内容。一朵偏离中心立着的云:

CSS动画概述

接下来我们来添加动画。添加CSS动画一共需要两步。首先,设置animation属性。然后定义keyframes并指定添加那些动画效果。

从上面的标签中,找到#bigCloud样式规则,添加如下的animation属性:

#bigcloud {
    animation: bobble 2s infinite;
    margin-left: 100px;
    margin-top: 15px;
}

animation这一行属性的意义我们暂时不需要理解,后面我们会讲到。然后,我们添加keyframes。@keyframes如下:

@keyframes bobble {
    0% {
        transform: translate3d(50px, 40px, 0px);
        animation-timing-function: ease-in;
    }
    50% {
        transform: translate3d(50px, 50px, 0px);
        animation-timing-function: ease-out;
    }
    100% {
        transform: translate3d(50px, 40px, 0px);
    }
}

添加了以上的样式规则后,再次预览页面。你可以看到白云已经愉快地跳动起来了。

刚刚都发生了些啥

我们先添加了一个CSS动画,使得白云跳动起来。现在,CSS动画到底是什么,也很简单。它允许你指定元素的某些特定属性(用于完成动画效果)的开始状态,任何中间状态(即关键帧),以及结束状态。上面例子中的云朵的移动非常简单,所以将它作为我们学习的入门,非常合适。

首先,我们看看animation属性:

animation: bobble 2s infinite;

animation属性用于设置动画。我们常用的是如上的简写,需要指定三个值:

  • 动画的名称
  • 动画时长
  • 动画循环的次数

我们的animation声明正确。动画名称为bobble,时长2s,并设置infinite无限循环。

关于前缀

animation属性目前还是非常新的(2013年),所以很多浏览器需要添加前缀才可以使用。不要把标签弄混了。另外,使用一些如-prefix-free library这样的工具可以保持标签简洁,同时也允许旧版的浏览器可以查看动画。

如你所见,animation声明并没有包含动画方面太多的细节内容。它设置的是比较高层面的定义,关于你的动画将会做什么,但是实质的CSS动画的声明,主要是放在@keyframes规则中。

接下来看看@keyframes规则:

@keyframes bobble {
    0% {
        transform: translate3d(50px, 40px, 0px);
        animation-timing-function: ease-in;
    }
    50% {
        transform: translate3d(50px, 50px, 0px);
        animation-timing-function: ease-out;
    }
    100% {
        transform: translate3d(50px, 40px, 0px);
    }
}

首先注意到,@keyframes规则的大概长这样。它的@keyframes声明后面连接了动画的名称 @keyframes bobble

然后,包含样式规则(即实际的关键帧),选择器是百分比0% 50% 100%,也可以是关键字fromto

@keyframes bobble {
    0% {
        transform: translate3d(50px, 40px, 0px);
        animation-timing-function: ease-in;
    }
    50% {
        transform: translate3d(50px, 50px, 0px);
        animation-timing-function: ease-out;
    }
    100% {
        transform: translate3d(50px, 40px, 0px);
    }
}

这些关键帧规则非常规范。它们包含了如transformanimation-timing-function的CSS属性,当@keyframes规则起作用时,这些属性的值也会被应用在元素上。有一个关于关键帧样式规则的内容我们需要注意一下。

我刚才解释的部分其实是很容易理解的,但是有块内容需要注意一下。尽管animation属性是在另一个样式规则中声明的,而关键帧则是在自己的@keyframes规则中声明的,它们是相互绑定的,缺少任何一个都不可以。

我们先看看animation属性和@keyframes规则是如何绑在一起的。

动画名称

@keyframes规则的名称充当了标识符的角色,animation属性用它来找到对应的keyframes:

#bigcloud {
    animation: bobble 2s infinite;
    margin-left: 100px;
    margin-top: 15px;
}
@keyframes bobble {
    0% {
        transform: translate3d(50px, 40px, 0px);
        animation-timing-function: ease-in;
    }
     
    50% {
        transform: translate3d(50px, 50px, 0px);
        animation-timing-function: ease-out;
    }
     
    100% {
        transform: translate3d(50px, 40px, 0px);
        animation-timing-function: ease-in;
    }
}

我们的animation属性引用bobble并不是一个巧合,因为我们的@keyframes规则名称也是bobble。如果这两个名称不一致,动画就无法工作。

动画时长和关键帧

从前面的部分,可以看到我们的animation属性和它的关键帧如何对应上。解决一个问题之后,我们来看看下一个。动画的时长和一个特定的关键帧规则实际作用的关系。

你还记得,当你在@keyframes规则中,定义关键帧样式规则,你的选择器并不是一个具体的时间值。而是一个百分比值或者fromto关键字:

@keyframes bobble {
    0% {
        transform: translate3d(50px, 40px, 0px);
        animation-timing-function: ease-in;
    }
    50% {
        transform: translate3d(50px, 50px, 0px);
        animation-timing-function: ease-out;
    }
    100% {
        transform: translate3d(50px, 40px, 0px);
        animation-timing-function: ease-in;
    }
}

在我们的例子中,关键帧选择器是百分比,0%50%100%。它们表示动画已经完成了多少。动画刚开始时,你的动画完成了0%。此时0%关键帧起作用。当你的动画进行到一半,50%关键帧起作用。当动画结束时,100%关键帧起作用。

关于fromto 选择器

如果不使用0%作为选择器,你可以使用from,两者等同。二100%则对应to关键字。我不太清楚为什么会有人用fromto,但是了解一下还是必要的。

我不是不支持fromto。只是它们的存在对我写CSS动画并没有什么影响。

animation属性中的duration值,除了设置动画运行的总时长,还帮助你根据实际的时间单位编写合适的百分比值。

下图是百分比值到2s的动画的时间单位的映射:

CSS动画概述

这对我来说也有点困惑。理解了duration是如何映射到单独的关键帧上之后,你就跳过了主要障碍——在脑海中想象动画如何进行。

接下来,我们来深入看看一个简单的CSS动画是如何工作的。你已经知道了如何使用animation属性声明一个动画,以及@keyframes规则中的关键帧样式是如何写的。我们也花了一点时间来学习动画是如何运行的。

还没完成,还有很多更详细的内容。

CSS animation属性详解

animation属性其实比我们刚刚看到的要更完整。 先尝试创建一个动画,然后了解animation属性。为了帮助学习,我们首先把下面这行简写换成完整的写法。简写版如下:

animation: bobble 2s infinite;

完整写法如下:

animation-name: bobble;
animation-duration: 2s;
animation-iteration-count: infinite;

简写版中的三个属性分别是animation-nameanimation-durationanimation-iteration-count。这些属性你应该已经非常熟悉了,所以我们来看看另外的我们还不熟悉的属性,如:animation-play-stateanimation-delayanimation-directionanimation-fill-modeanimation-timing-function

开始!

暂停、恢复一个动画

默认情况下,你的动画开始时,你的animation属性中的样式规则也就起作用了。简单来讲,也就是页面加载的时候。首先,我们简单想象一个2s的动画,并设置了无限循环:

CSS动画概述

每个黄色的矩形表示你的动画的一次迭代,也就是一个矩形表示执行了一次动画。如果你把每个矩形并排放置,结果如上图。

一旦动画开始,它不会停下,直到动画结束。如果你的动画设置了循环,它就会在每一次结束的时候又立刻重新开始。每一次循环用一个单独的黄色矩形表示。我们目前的bobble动画就是这样。

有时候,你可能不想要这样。如果你希望动画不要在样式规则激活时立刻执行,或者你想要在中间某个时刻暂停动画,你可以尝试animation-play-state属性。这个属性允许你切换动画的状态为runningpaused,无论动画是否正在进行。

默认情况下,animation-play-state属性的值为running。你可以设置为paused来暂定动画。

animation-play-state: paused;

当动画暂停时,它会保留动画的所有最后的计算值:

CSS动画概述

就好像时间突然静止了。你可以通过设置animation-play-state属性来恢复running。恢复之前,动画不会回到0%的位置突然重新开始:

CSS动画概述

你的动画将平滑地从你暂停的位置继续,和媒体播放器的播放和暂停键一样。

动画的延迟delay和偏移量offset

如果你想让你的动画在某一段时间内不要播放,你可以看看animation-delay属性。这个属性可以让你指定动画播放的延迟时间。

animation-delay: 5s;

延迟不是发生在0%关键帧开始后,等待5s。而是在0%关键帧之前,在动画第一次迭代之前:

CSS动画概述

一旦动画开始运行,延迟的值将不会再起作用。动画的每一次后续的迭代(如果还有),都是一个结束另一个马上开始,没有延迟。

现在,这里有一些关于这个属性的内容。animation-delay的值除了可以是正数,也可以是负数:

animation-delay: -.25s;

当你指定了一个负值,你的动画就会马上开始,但是会相对你指定的duration有一定的偏移。如果animation-delay的值为-.25s,结果如下:

CSS动画概述

负值代表一个信号,告诉浏览器将这个值作为偏移量,而不是延迟。是的,这有一点奇怪,特别是这个属性明明叫animation-delay。比较不奇怪的是——如果你指定的偏移量比动画一次迭代的时长还要大的话,那这就不是问题了。你的动画将会从起点落在的迭代中开始(比如起点出现在第二次迭代的时间段中)。只要确保你的动画有足够多次的迭代,可以包容起点。如果你没有足够的迭代,你又指定了一个比较大的偏移量值,你的动画就无法运行。

保留关键帧指定属性值

如果你没有让动画循环,你会注意到一旦动画结束,关键帧设置的属性都会移除,你的元素就回到动画播放前的状态。这是因为你通过关键帧应用的属性都是暂时性的。当关键帧起作用时,这些属性值就是存在的,一旦离开了那个窗口,这些属性值都不会保留。如果你不希望这种行为,你的动画可能会在结束的时候突然跳到初始位置或者突然重置。我们先来看看两个例子,然后看看如何改变这种默认行为。

等待开始

第一种情况发生在,当你处理一个animation-delay时。例如,你指定了5s的延迟:

CSS动画概述

你的动画会等待5s,你的关键帧都不处于激活状态。第一个关键帧包含的所有属性,在这5s的延迟中都不会被激活。

动画完成了

第二种情况是你的动画已经完成后,尝试指定动画循环三次:

CSS动画概述

最后,在第三次迭代的最后一帧,所有指定的属性都会消失。应用了动画的元素会回到动画没开始前的状态。

看看animation-fill-mode

如果你希望开始的关键帧的属性在delay期间就激活,或者最后的关键帧的属性在动画结束之后仍然保持,你可以通过设置animation-fill-mode属性来完成。如下:

  • none:如果你希望某个关键帧的属性值应用,首先你的关键帧应该处于激活。
  • forwards:在你的动画完成之后,所有的属性都保留结束时的值。
  • backwards:动画会应用开始关键帧设置的属性值,即使关键帧还没有激活。
  • both:forwardsbackwards的结合。

我创建的动画是永远循环的,开始的时候没有延迟。我创建的很多动画的起始关键帧、结束关键帧、元素的非动画状态的属性值之间都没有很大的差异。因为这样,我从来不纠结上面的问题,所以在animation-fill-mode属性声明这里不要感到压力。

动画反向(或反转动画方向)

现在,我们来看看另一个属性。默认情况下,动画从0%按照顺序播放到100%。你可以通过设置animation-direction属性来改变这种行为,无论是正常normal、反转reverse、交替alternate或者反转交替alternate-reversenormalreverse都相对简单,所以我们来看看另外两个比较有趣的值:alternatealternate-reverse

当你将animation-direction的值设置为alternate,你的动画开始时正常的。第二次迭代的时候,它就变成反方向的,然后接下来都在正序和反序中交替:

CSS动画概述

animation-direction设置为alternate-reverse,它和上一个值相似但是有一点不同:

CSS动画概述

动画开始是反向的,然后在正序和反序之间交替。

**注:**原文为alternate先反向后正向,alternate-reverse先正向后反向,有误,已改正。

最后是时间函数

最后一个动画相关的属性是animation-timing-function。这个函数允许你指定开始和结束之间的动画时间函数。我在Easing Functions in CSS3一文中介绍了一些功能函数,欢迎了解。

animation简写

我们目前看到的属性都是完整的写法:

#somethingSomethingDarkSide {
    animation-name: deathstar;
    animation-duration: 25s;
    animation-iteration-count: 1;
    animation-play-state: paused;
    animation-delay: 0s;
    animation-direction: normal;
    animation-fill-mode: both;
    animation-timing-function: ease-out;
}

有些同学可能比较喜欢简写,也就所有的属性以及值都在animation属性中指定。事实上,我前面的bobble动画也是简写的:

animation: bobble 2s infinite;

所有你上面看到的完整写法的属性,都可以在简写中对应——对应关系如下:

animation: <animation-name> <animation-duration> <animation-timing-function> <animation-delay> <animation-iteration-count> <animation-direction> <animation-fill-mode>;

斜角括号中就是对应的属性。注意animation-play-state属性不能简写,你需要另外单独写。

无论如何,把完整写法变为简写,结果如下:

#somethingSomethingDarkSide {
    animation: deathstar 25s ease-out 0s 1 normal both;
    animation-play-state: paused;
}

简写版确实比完整版要方便很多,但是还是看个人(或团队)偏好吧。

我个人比较喜欢简写版来指定animation-nameanimation-durationanimation-timing-function,因为比较好记。一旦超过了三个属性值,我就需要查阅文档了_(:зゝ∠)_。

你的情况可能要根据你个人的情况咯,所以选择适合自己的。还有,我们接下来应该看看keyframes部分了。

关于keyframes

我们大部分的时间都花在了animation属性以及它对动画的影响上。实际上CSS动画最大的功臣是keyframes,所以接下来看看keyframes吧。

再次看看bobblekeyframes

@keyframes bobble {
    0% {
        transform: translate3d(50px, 40px, 0px);
        animation-timing-function: ease-in;
    }
    50% {
        transform: translate3d(50px, 50px, 0px);
        animation-timing-function: ease-out;
    }
    100% {
        transform: translate3d(50px, 40px, 0px);
    }
}

我前面提到的,一个单独的关键帧差不多相当于一个样式规则。你把CSS属性放进去,这些属性在关键帧执行到的时候就被激活。需要注意的是,不是每一个CSS属性都可以放在keyframe里面。只有那些可以用来做动画的CSS属性以及animation-timing-function才可以。

可以在keyframe中使用的属性,这里有一个列表可以查看。还有一些额外的,点击这里

最后要看的是animation-timing-function属性,你可以在keyframe中指定它。这个属性的作用是,从你当前所在的关键帧到下一个关键帧之间的时间函数。在我们的例子中,在0%关键帧的位置,我们的animation-timing-function设置为ease-in

@keyframes bobble {
    0% {
        transform: translate3d(50px, 40px, 0px);
        animation-timing-function: ease-in;
    }
    50% {
        transform: translate3d(50px, 50px, 0px);
        animation-timing-function: ease-out;
    }
    100% {
        transform: translate3d(50px, 40px, 0px);
    }
}

这个时间函数在你的动画处于0%~50%之间时被激活。同样,在50%关键帧中声明的animation-timing-function也会在50%~100%之间激活。也就是这个时间函数是在当前关键帧到下一个关键帧之间工作的,所以在100%关键帧处声明时间函数是没有必要的。

重用Keyframes

我要讲得最后一个东西是使用相同的关键帧来用于另一个动画的声明。我前面提到animation属性声明,和实际的@keyframes规则是分开的,这对于动画其实是有点笨重的。但是如果你尝试了,还是有好的东西的。

就是你可以在另一个animation属性声明中重用相同的关键帧。我们下面来扩展上面的例子来看看。

在原来的HTML文档中,只包含了一朵云,而且是弹跳的,我们现在再加一朵。

<!DOCTYPE html>
<html lang="en-us">
 
<head>
<meta charset="utf-8">
<title>Bouncing Clouds</title>
<script src="//www.kirupa.com/js/prefixfree.min.js"></script>
 
<style>
#mainContent {
    background-color: #A2BFCE;
    border-radius: 4px;
    padding: 10px;
    width: 600px;
    height: 300px;
    overflow: hidden;
}
.cloud {
    position: absolute;
}
#bigcloud {
    animation: bobble 2s infinite;
    margin-left: 100px;
    margin-top: 15px;
}
#smallcloud {
    animation: bobble 4s infinite;
    margin-top: 65px;
    margin-left: 200px;
}
@keyframes bobble {
    0% {
        transform: translate3d(50px, 40px, 0px);
        animation-timing-function: ease-in;
    }
    50% {
        transform: translate3d(50px, 50px, 0px);
        animation-timing-function: ease-out;
    }
    100% {
        transform: translate3d(50px, 40px, 0px);
    }
}
</style>
</head>
 
<body>
    <div id="mainContent">
        <img id="bigcloud" alt="#" class="cloud" height="154" src="//www.kirupa.com/images/bigCloud.png" width="238">
        <img id="smallcloud" alt="#" class="cloud" height="103" src="//www.kirupa.com/images/smallCloud.png" width="158">
    </div>
</body>
 
</html>

添加#smallCloud样式规则,以及第二个img元素,然后预览页面。如果没出什么错的话,你会看到两朵云在愉快地弹跳...也就是这篇文章开头的实例。

现在你的实例可以工作了,我们来看看如何做到的。就是#smallCloud样式中的animation声明:

#smallcloud {
    animation: bobble 4s infinite;
    margin-top: 65px;
    margin-left: 200px;
}

注意我们引用的是相同的@keyframes规则,名称是bobble。两个动画声明唯一的不同就是#bigCloud的时长是2秒,而另一个是4秒:

#bigcloud {
    animation: bobble 2s infinite;
    margin-left: 100px;
    margin-top: 15px;
}

这意味着你在bobble keyframes中定义的属性,被应用在了两朵云上。唯一的不同是一个时长为2s,一个为4s

CSS动画概述

keyframesanimation声明之间相互独立的特性允许你这样做。任何你在animation属性中的声明都会在另一个层面影响你的keyframes的运行,比如前面看到的duration。每个动画属性我前面都做了简单的介绍,可以影响你的keyframes的行为,而不需要直接接触关键帧。

这点你必须承认,很方便。

声明多个动画

最后一件我们要看的事情是,如何在同一个animation属性在中声明多个动画。在简写版的声明中,直接用逗号分隔每个动画即可。

#oppaGangnamStyle {
    animation: hey 2s infinite, sexy 1s infinite, lady 5s infinite;
}

注意每个动画都指向不同的@keyframes规则。如果由于某些原因,你要在同一个animation属性中指定相同的@keyframes规则,基于CSS的优先顺序,后面的动画会先执行。

译注:同一个元素应用多个keyframes动画,是同时执行的;如果前后应用了相同的keyframes,那么放在后面的先执行,再执行前面的。

如果是完整版地声明动画,如下:

#oppaGangnamStyle {
    animation-name: hey, sexy, lady;
    animation-duration: 2s, 1s, 5s;
    animation-iteration-count: infinite;
}

同样是非常简单的。在CSS中都使用逗号分隔,所以当要为一个属性声明多个值的时候,就加个逗号吧,这个方法是值得尝试的。

结论

CSS中的animation属性是一个非常重要的属性,无论是简单了解还是深入学习——特别是如果你想让你的界面内容更lively。学习了如何使用CSS动画方面的基础之后,可以再看看下面的文章:

关于CSS动画更详细的讲解可以点击here查看O(∩_∩)O


本文根据@kirupa的《All About CSS Animations》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://www.kirupa.com/html5/all_about_css_animations.htm

彦子

在校学生,本科计算机专业。逗比一枚,热爱前端热爱生活,喜欢CSS喜欢JavaScript喜欢SVG,爱玩PS玩AI玩啊逗比的软件。努力向上,厚积薄发。

如需转载,烦请注明出处:https://www.fedev.cn/animation/all-about-css-animations.htmlAir Jordan VII 7.5 Ture Flight