CSS动画详解

闲聊 无标签
0 728
WIZ
WIZ 自成一派 2023-01-14 21:20:19
Lv:36级

动画概述

在制作一款产品时,对于用户能感知的部分,总是投入了很多精力和时间来让页面变得如何如何漂亮。可以设计最顺眼的布局,优雅的排版,美丽的图片(图标)等等的手段。

这看起来已经很完美了,但似乎还不够,此时的页面仅仅只是死的,没有活力,如果我们加入动画呢?

动画能使页面变得活泼,如果动画的加入合适,能让用户像在逛商场一样,总是充满愉快、活力的体验。

动画并非仅仅是为了好看与炫酷,她有时能暗示用户,帮助用户更好地理解你的产品。

现实中的动画

屏幕和现实不一样,屏幕无法产生物理运动,屏幕上永远只能显示1帧

所谓的1帧,其实就是一张静止的图片,如果连续播放多张有关联的图片,那么在人眼里就造成了运动的假象,人的大脑能自动脑补出动作,一秒内播放的帧越多,画面就会显得越流畅。

CSS里的动画

在开始讲解之前,务必要先给你讲讲核心原理,就如你是一个汽修修理工,总得先知道一个概念

汽车之所以能动,是因为里面有个马达。

css里的动画,其实很多情况下,都是在几个关键帧之间转换(帧动画),或者说几个数值之间变化(补间动画)

  • 帧动画是靠每一帧有关联的画面快速切换,来形成动画(如上方北极熊)
  • 补间动画:你指定几个关键值,计算机便能动态生成两个值之间的过渡值

如这个例子:

你观察一下上方这个长方形的变化,你会发现,只是高度在变化,变到最高和最低

确实是这样的

@keyframes aa{ 0%{ height: 0px; } 100%{ height: 200px; } }

0%的时候高度是0px,100%的时候是200px,我只指定了关键值,关键的两个数值,浏览器便能动态变化了。

举个不恰当的例子:

你爸爸此时正在家看金碟豹,对一旁的你说:去商店给我买包烟

此刻的你在家,而你爸叫你去商店,这是两个不同的地点,但你却能顺利地从家前往商店,并从商店再返回家。

这是为什么?因为你知道沿着某条路一直走便能走到商店,从商店回来也是同理。

而你给的初始值是0px,最终值是200px,计算机自然也能计算0px到200px之间的值。

这用专业的词叫:补间动画,补间,计算机补的就是中间的值,一种状态平滑变到另外一种状态。

所以你现在必须脑海中得有一个概念:只要设置好关键的值,计算机便能给我生成连贯的动画效果

再举个例子:

就像50米赛跑,有起跑点和终点,裁判规定好起跑点和终点,而运动员是不是就能自觉地从起跑点跑到终点了?

而你此刻就是那个裁判,你指定好起跑点和终点,而浏览器就能自动帮你从0米一直跑到50米

而你这个裁判更离谱,你还能控制这人50米什么时候开跑,跑几遍,在多少时间内完成等等命令。

而浏览器就相当于那个选手,只能受你摆布,老老实实按照你的命令跑步(这不就是程序员?)。

补间动画,浏览器能补哪些呢?高度、颜色、角度、坐标、透明度…等等都能补

下面这个例子变了颜色,我指定了三个关键的颜色(三个关键的值):

@keyframes aa{ 0%{ background: red; } 50%{ background: green; } 100%{ background: blue; } }

你看浏览器就能自动用这三个颜色,不生硬很平滑地变化了。

两种方式

在日常开发中,一般会借由两种方式产生所谓的动画

  • transition
  • animation

transition:一般叫过渡,需要事件触发(不是JavaScript里的事件),比如:hover,鼠标移上去就触发,只能设置两个关键值,初始和结束。

animation:这个叫动画,不需要事件触发,写好了就能执行,操作有很多(下面重点会讲),可以设置相当多的关键值(关键帧)。

transition

虽然大家叫过渡,但也是属于能动的,顺便也讲了。

先来一个例子:

左边第一个矩形关键代码很简单:

div { background: red; } div:hover { background: blue; }

div刚开始是红色的,当鼠标移入之后,就里面变为蓝色

这看起来功能挺正常的,但看着似乎有些生硬了,红色变为蓝色这个过程,是瞬间完成的,很是突兀。

如果加上 transition属性

div { background: red; transition: 0.4s; } div:hover { background: blue; }

现在你把鼠标移入第二个红色盒子,颜色的变换就变得比较自然了,因为浏览器自动给你补全了红色到蓝色之间的颜色,当存在连续变化(线性)之后,看起来就像在动一样,比较理所当然。

浏览器为何能补全两个颜色?颜色其实也是数值,在css里面,可以通过特定的英语单词来描述,比如:red、green、blue、orange之类的。

还可以通过以下方式:

十六进制表示颜色:#ff0000

RGB表示颜色:rgb(255,0,0)

HSL表示颜色:hsl(360,50%,50%)

四种方式,看你喜欢,我一般爱用十六进制。

transition有一些子属性:

属性
transition-property动画展示哪些属性,可以使用all关键字
transition-duration动画过程有多久
transition-timing-function控制速度变化
transition-delay动画是否延迟执行

transition-property:你的哪个部位变了,你就填哪个,像上一个例子中,只有颜色变了,

所以transition-property:color;就行了,高度变了就填高度,如果有多个变了,可以加入逗号,比如
transition-property:color,height;这里代表着颜色和高度有变化,也可以直接填入all,表示全部。

相当于是一个检测器,当检测到填入的属性变了,就给加过渡效果,如果填入的是color,哪天color的值变了,就会给你整个过渡效果,如果哪天高度变了,那抱歉了,没有过渡效果,因为transition-property里没指定高度。当你填入all时,但凡有点风吹草动,都加入过渡效果。

transition-duration:填入时间,单位是秒,比如:transition-duration:0.4s,表示动画时常只有0.4秒。

也可写成transition-duration: .4s,小数点前的0可以省略。


transition-timing-function:动画的速度变化,比如大家常说的九浅一深,先快后慢再贼快之类的,css内部提供了一些英语单词:linear,ease,ease-in,ease-out,ease-in-out;也可以用贝塞尔曲线函数cubic-bezier()控制动画的速度变化。


transition-delay:等待一下,再执行动画,比如:

transition-delay:2s;等两秒才执行动画。


这四个单独的属性,是可以组合在一起写

transition-property:width; transition-duration:5s; transition-delay:2s; /*组合,一句话完成*/ transition:width 5s 2s;

写的时候,可以问自己transition:哪个变?变多久?怎么变?等多久变?

日常开发中,如果不是有特殊的需求,往往我们在写的时候,只会写第一个和第二个,也就是哪个变和变多久

比如:transition: width 5s;

我只写了个宽度变和变五秒,其他属性可以不写,因为浏览器会给默认值。

animation

先看看整体大概结构:

div { animation: wch 3s; } @keyframes wch { 0% { color: #000; } 100% { color: #fff; } }

animation: change 3s;是动画的第一部分,用来控制动画的各个规则,比如:延迟、次数、时间等等,上面这个代码中指定了动画效果和时长。

@keyframes wch{…}是动画的第二部分,里面主要是写动画各个关键值(关键帧),是以百分比的形式,你可以理解为一个进度条,走到0%了执行某某某,走到23%了执行某某某,走到50%了执行某某某,走到100了执行某某某。

和上面的过渡属性transition一样,浏览器都能给你在每个关键帧中间生成平滑过渡效果,如上方0%时是#000(黑色),一直到100%时的#fff(白色),这之间的变化是由浏览器自动生成的(你不用纠结他是根据什么原理生成的)。

animation的子属性有:

属性
animation-name指定由 @keyframes 描述的关键帧名称。
animation-duration设置动画一个周期的时长。
animation-delay设置延时,即从元素加载完成之后到动画序列开始执行的这段时间。
animation-direction设置动画正向运行还是反向运行等等
animation-iteration-count设置动画重复次数, 可以指定 infinite 无限次重复动画。
animation-play-state允许暂停和恢复动画。
animation-timing-function设置动画速率,比如先快后慢
animation-fill-mode指定动画执行前后如何为目标元素应用样式。
@keyframes 规则设定关键帧

其中,对于一个动画:

  • 必须项:animation-name、animation-duration 和 @keyframes规则

  • 非必须:除上面三个,其余的都有默认值,可以不写

下面开始详细讲解各个属性

animation-name / animation-duration

animation-name的作用,只是和@keyframes 规则绑定的,让浏览器把你写的animation和@keyframes对应上

div{ animation: bb 2s; } @keyframes aa{ 0%{ background: #000; } 100%{ background: #fff; } } @keyframes bb{ 0%{ background: #000; } 100%{ background: #fff; } }

这里的div里的动画,会去找bb那个,而不会去找aa,有的时候一个页面要写很多组@keyframes 集合,肯定要用名字区分一下谁是谁。

animation-duration的作用是设置动画时常,上方代码中,设置的动画时长是两秒,这个属性单位是秒。

animation-delay

延时,也就是等多久才执行,延迟几秒执行,直接看效果:

第一个长条立马冒出了,第二个等了一秒才开动。

核心代码:

<div></div> <div></div> div{ animation-name: aa; animation-duration: 2s; } .vv{ animation-delay: 1s; } @keyframes aa{ 0%{ width: 0vw; } 100%{ width: 100vw; } }

上面这个小代码,其实可以用一句话表示,用熟练了其实不用单独写animation的子属性了,可以全部写在一起

animation: aa 2s 1s

表示为:动画的名字、时长、延时

动画的延时,时长可以填入负数,时间可以用负数来表示吗?其他地方也许不行,但在css的动画里,是可以的。

填入负数时间表示的含义为:提前x秒执行

举个例子,还是上边那个五十米跑步,裁判在喊开始的前五秒的时候,你就开跑了

我假设每个运动员的速度一样,都是一秒跑10米

当裁判正式喊跑的时候,你是不是已经领先其他选手50米了?

其实,时间是不会提前的,那浏览器怎么表现出这个动画是提前执行的呢?

假设现在我们要在2秒内,从1一直变到200,正常情况下,就是老老实实的从123456789一直到200,这个过程持续两秒

如果我提前1秒变呢?从客观上看,其实我根本没有提前,只是在动画开始执行的时候,我不是从0开始数的,我是从一秒那时刻开始数的,1数到200需要花费2秒,那在一秒的时刻,正常数,是100,那么我就是从100那开始数的。

可能讲的不是很好,再换个例子,我和我弟弟需要前往学校,需要经过A、B、C、D才能到,学校七点半上课。我那弟弟喜欢七点整点出发,而我起得早,我六点半就出发,弟弟走到A的时候,而哥哥我已经走到C了,而浏览器是站在我弟弟的视角的:我才出发,这哥哥怎么就瞬间变到C点了?

来个实际例子:

如果你想实现这个效果,有两种思路

  • 写三组动画,把这三个球初始角度分别设置0度120度240度,这样在转的时候就可以同时转了
  • 只用一组动画,3 个球的其中两个提前整个动画的 1/3,2/3 时间出发

第二种思路开始实施了:

.qiu1 { animation: rotate 3s infinite linear; } .qiu2 { animation: rotate 3s infinite 1s linear; } .qiu3 { animation: rotate 3s infinite 2s linear; }

上述代码表示的意思为:动画名字叫rotate 动画时长3秒 动画次数无限 动画速率是匀速

代码中的infinite表示动画无限循环,linear表示动画是均速的,下面会讲。

动画效果不太对,动画一开始时,三个球没有一起动,只有等了两秒后,三个球才一起在动。

现在我们用到负数的延迟吧:

.qiu1 { animation: rotate 3s infinite linear; } .qiu2 { animation: rotate 3s infinite -1s linear; } .qiu3 { animation: rotate 3s infinite -2s linear; }

当第一个球开始动的时候,第二个球已经提前跑到120度的位置(提前一秒),而第三个球由于是提前两秒,则早就跑到240度的位置了,所以当第一个球开始动的时候,此瞬间,三个球的夹角已经变成了120度,是我们想要的效果,已经变成我们想要的形状了。

值得一说的是,第二个第三个球,写了提前x秒执行,其实压根就没有提前执行,这里说的提前,实际上只是瞬间移动到了x秒的位置,动画效果是没有提前执行的,只是数值跑到了x秒的数值去了,所以在我们的眼里,他是瞬间出现在那个位置的(至于为什么说提前,那是先对于0秒来说的,我才开始掏出作业本,你就要抄完了,你是不是提前偷抄了?)。

正常:123456

提前两个数:3456

animation-timing-function

控制动画的速率,是先快后慢,还是先快一点再慢一点最后再快一点还是…

正规的说法是:它定义了动画在每一动画周期中执行的节奏。

css官方支持一些特殊的英文单词参数:

  • ease //动画以低速开始,然后加快,在结束前变慢
  • ease-in //动画以低速开始
  • ease-out //动画以低速结束
  • ease-in-out //动画以低速开始和结束
  • linear // 匀速,动画从头到尾的速度是相同的

这里就不演示效果了,直接放张图:

除了这五个关键字外,还支持三次贝塞尔曲线,用 cubic-bezier()方法,比如:

animation-timing-function: cubic-bezier(0.1, 0.7, 1.0, 0.1);

贝塞尔曲线,网上有现成的工具生成:点击访问,有了贝塞尔曲线,就可以为所欲为了,不局限于官方提供的五种速率。

网上有个很直接的例子:点击访问,可能需要科学上网

animation-timing-function除了内置五个速率和贝塞尔曲线的方法外,还内置的一个函数:steps函数

这个函数,一般拿来做逐帧动画,也就是这种:

这北极熊在跑,跑的这个动作,其实是由八个单独的姿势组成的。

当连续播放的速度够快,就能造成一种视觉上的连贯性。

假如我们有下面这种图片:

我该怎么做才能让这小人动起来呢?

而steps函数是将整个动画过程分为指定的步数,举例的话:

大富翁,摇塞子走格子,摇到6就一格一格地跳6格

假设我写了一句:steps(6)的意思就是将设定的 @keyframes 动画分为 6 段执行,而整体的动画时间是 0.6s,所以每一段(这里也可以理解为是每一帧)的停顿时长为 0.1s

拿上面那个小人来说,通过观察,我们知道,小人的动作被分为了6种姿势,我们如果用steps(6)把动画分成6次6帧,而每帧如果刚好对应上图的一种姿势,那是不是就把这小人的6种姿势给连贯起来了?

<div></div> div { width: 256px; height: 256px; background: url(./sprite.png); background-size: cover; background-position: 0px; animation: sprite 0.6s steps(6) infinite; } @keyframes sprite { 0% { background-position: 0 0; } 100% { background-position: -1536px 0; } }

用文字可能不是很好叙述,我录制了一个视频

animation-play-state

这个属性很显然,控制动画运行和暂停的,

它能填的值有两个:paused和running,默认情况下是running

上面这个小例子,鼠标放到文字上,方块就能停下

核心源码:

<div class="aa">停!</div> <div class="bb"></div> <style> .bb{ animation: move 2s infinite alternate; } @keyframes move { 100% { transform: translate(100px, 0); } } .aa:hover + .bb { animation-play-state: paused; } </style>

animation-fill-mode

控制元素在各个阶段的状态,各阶段?CSS动画都有哪些阶段?

  • 初始状态,此时就是没有触发动画时,元素由css其他属性控制
  • 等待期,就是animation-delay 设置的延迟期间
  • 动画执行期,动画在运行时
  • 完成期,动画结束了的那一刻

animation-fill-mode有四个选项:none、both、backwards、forwards

none 表示 等待期和完成期,元素样式都为初始状态样式,不受动画定义(@keyframes)的影响。

both 表示 等待期样式为第一帧样式,完成期保持最后一帧样式。

backwards 表示等待期为第一帧样式,完成期跳转为初始样式

forwards 表示等待期保持初始样式,完成期间保持最后一帧样式。

可能有点不好理解,下面我用视频来讲解一下:

(如果你懒得看视频,你也可以直接看网上的一篇文字攻略,点击访问,我就是在这领悟的)

我个人的理解:这个属性主要用来控制元素在运行这个动画之前之后的事

执行动画之前如何?

执行完动画之后如何?

animation-iteration-count/animation-direction

这两个一起讲,

  • animation-iteration-count:控制动画运行的次数(数字或者infinite无限)
  • animation-direction:控制动画的方向,(正向或者反向,正反交替,反正交替)

animation-iteration-count,填入数字就代表动画执行几次,填入infinite则会一直执行,如果不指定默认动画只执行一次

animation-direction,默认是正向播放,也就是从0%一直到100%;若是填入reverse,动画则会先从100%然后再慢慢地执行到0%。

至于所谓的正反交替,也很好理解:

你从家到学校,

然后从学校返回家,日复一日

从家到学校类比为:从0%到100%,

从学校返回家类比为:从100%到0%

这就是一次正反交替

在有的时候,如果你的动画没有写正反交替,会造成一个现象:当动画执行到100%之后,瞬间就变到0%了,这个过程是瞬间完成的,会让用户感到很僵硬。

下方这个小例子,第一个红条,每次都在100%时瞬间变成0%;而第二个红条,跑到100%之后会慢慢变0%,然后又再从0%变到100%,给人一种过渡平滑的感觉。

alternate-reverse表示反正交替,也就是先从100%到0%,在从0%到100%

还是列出官方的说法吧,免得你的理解和我产生偏差了:

属性
normal默认值。动画按正常播放。
reverse动画反向播放。
alternate动画在奇数次(1、3、5…)正向播放,在偶数次(2、4、6…)反向播放。
alternate-reverse动画在奇数次(1、3、5…)反向播放,在偶数次(2、4、6…)正向播放。

动画的建议

假设我们要实现这么一个效果:

黑色方块,有下滑的动画,也有透明度的变化

我希望你在实现的时候,多写几组keyframes,如:

div { animation: falldown 2s, fadeIn 2s; } @keyframes falldown { 100% { transform: translate(0, 150px); } } @keyframes fadeIn { 100% { opacity: 0; } }

建议别把一堆变化全写在一个@keyframes来实现动画效果,这个例子还好,如果你未来的动画很复杂,这将变得难以梳理。各个分开写,可以做到对每一个属性动画做到精准的控制。

一个标签是可以指定多个@keyframes的。每个动画直接用逗号连接

如果你想把animation的各个子属性全写在一行,先后顺序为:

animation: name duration timing-function delay iteration-count direction; 名字 时长 速率 延时 次数 方向 /*比如:*/ animation: wch 5s infinite; /* 用不到的子属性可以不写,浏览器会给默认值 */

keyframes 的格式

使用百分比:

@keyframes wch { 0% { opacity: 1; } 100% { opacity: 0; } }

使用英文字符:

@keyframes wch { from { opacity: 1; } to { opacity: 0; } }

在写英文字符时,from 等同于 0%,而 to 等同于 100%

如果你的动画不止两帧,还是用百分比的形式吧。

最后

如果你认认真真学了这一篇CSS动画之后,希望你不要产生学完之后就能立马做出各种很炫酷的特效的想法

这里并没有否非花哨的效果,只是因为做出这种花哨的效果不容易

  • 想象力
  • 技术

做动画也是做设计,这需要一定的想象力,这似乎要看个人了,我觉得没经过培养的普通人的灵感应该没那么变态吧?

其次就是技术,你得非常了解CSS的各种属性;还得具备一些格外的技能,比如有时还要借由错觉,想方设法欺骗观众的眼睛之类的,这种非CSS方面的技能。

这些都需要慢慢积累的,一时做不出也无妨,慢慢来,别一来就分析别人复杂的动画,给自己添堵,懂得循序渐进。想制作出让人赏心悦目的页面,讲究一个合适观感。好比是一个班级,成绩最好的那位同学全年级第一,但学生的整体水平却不咋地,这能体现出一个老师的授课水平吗?

说到底动画效果只是辅助,属于锦上添花,页面观感或者功能不咋地,就算加入很炫酷的动画,也只是属于屎盆子镶金边,东施效颦罢了。

当然这只是我的一家之言,我并非专业的人,这只是我结合我的见闻而得出的结论。

看完这篇CSS动画文章后,也莫局限于CSS,JavaScript也可以做出好看的效果,不一定非得用CSS里transition和animation。

最后的最后

这篇文章参考了知乎上的大佬点击访问、菜鸟教程、CSDN、segmentfault等等,

写完这篇文章,使我对CSS里的这俩动画属性有了更深的认识了,特别是某些属性,从陌生到现在认识。这篇文章算是我写过目前来说应该算是篇幅最长的一篇了。

文章中的例子,我都提供了源码点击查看,在学习的时候,可以用我的源码进行调试,知其然,知其所以然。

你的想法与我的表达有时难免同步(这很正常,每个人思考的角度和侧重点可能不一样),如果有疑问,评论区留下你的疑惑,文章中如有错的欢迎批评指正,有好想法也可以评论区交流。

楼主签名:DNSWIZ 站长故事
回帖
回复列表

    遵守各国法律法规 严谨违规内容

    • QQ群:1140251126
    • Email:m@max.ooo
    • 本站可以自由发布正规网站外链
    • 本站域名皆为闲置域名可出售
    本站热帖
    01 ch.gs正在竞价 一起搞它 188
    02 域名行情兵败如山倒,势如破竹。 182
    03 SSL证书是不是个人网站的绊脚石 164
    04 微信开放个人注册服务号了没有门槛了? 162
    05 刚看到个xinyi.net过期了在抢注,我本地的生活站也倒闭了 155
    06 每一个都是好域名 149
    07 删除友情链接是不是友尽了? 148
    08 当前可用的开源论坛程序整理[2024] 141
    09 今天都在搞欧洲开放大学MBA证书 140
    10 频繁在各大域名群叫卖,其实和乞丐没什么区别! 135
    推荐主机