CSS动画详解

闲聊 无标签
0 535
GS
GS 自成一派 2023-01-14 21:20:19
Lv:27级

动画概述

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

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

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

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

现实中的动画

屏幕和现实不一样,屏幕无法产生物理运动,屏幕上永远只能显示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里的这俩动画属性有了更深的认识了,特别是某些属性,从陌生到现在认识。这篇文章算是我写过目前来说应该算是篇幅最长的一篇了。

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

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

楼主签名:Admin.Gs 站长故事
回帖
回复列表

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

    • QQ群:1140251126
    • Email:admin@admin.gs
    • 本站可以自由发布正规网站外链
    • 本站域名 admin.gs 其它皆为闲置域名
    本站热帖
    01 彩虹网站监控可以网址url监控,定时任务源码 675
    02 域名真的不行了,往年卖出去的又可以注册了 621
    03 携手特工、雷人组建了nice臻选 545
    04 码支付漏洞测试开源版,来测试有没有漏洞 540
    05 源支付V7版,以前150购买,需要的下载研究,拿走回复 437
    06 只秀不卖 429
    07 免费收费域名 429
    08 我还有6个域名。 429
    09 最新版弹幕播放器源码,带后台 424
    10 免费域名申请地址 384
    推荐主机