09.变形、过渡和动画
09.变形、过渡和动画
9.1 变形
- 变形的效果可以通过网站查看
- 变形只是对元素本身的可视区域显示效果进行处理,不会触发回流重排,同时是交给合成处理,使用
GPU渲染,效果很快,常见的可以用于轮播图,代替会触发回流的left和top修改
9.1.1 坐标系
- 变形涉及两种坐标系
笛卡尔坐标系,用户是从z轴正方向向里查看,x轴正方向是右侧,y轴正方向是下侧

笛卡尔坐标系 球坐标系,在笛卡尔坐标系外面添加一层球坐标系,变形允许元素绕坐标系的一轴旋转,实际上都对应着轴在轴所在的圆周平面上的旋转一定角度

球坐标系
9.1.2 变形属性
变形其实只有一个属性,不过有几个辅助属性用于控制如何变形。先从主要的属性
transform入手,接收一个空格分隔的、定义不同变形函数的列表,初始值是none即不变形需要注意的是
- 虽然经过缩放的元素可能比变形前小或大,但是元素在页面上所占的空间与变形前保持不变。所有的变形函数都是如此:当你平移或旋转一个元素时,它的兄弟姐妹不会自动移开
- 如果使用CSS变形一个svg元素,变形的范围框是svg定义的对象范围框
- 所有经过变形的元素都有自己的层叠上下文
- 变形函数一次只处理一个,从第一个(最左边)开始,一直到最后一个(最右边),不同的变形顺序结果可能会不一样(比如基准不同,百分比效果不同)
- 当你有一系列的变形函数时,所有的函数都必须是正确的格式化;也就是说,它们必须是有效的。如果哪怕只有一个函数是无效的,它就会使整个值无效
- 变形是不会叠加的,也就是说如果希望定义额外的变形效果,需要在变形中重复声明之前的变形效果,否则之前的效果将被覆盖
- 变形不基于行内框完成,也就是说,行内元素只能随父块级容器变形,行内元素如何变形,是每行单独变形还是一起变形还在讨论中
变形属性接收一个变形函数列表,变形函数有一下几类
- 平移变形函数
transalate - 缩放变形函数
scale - 旋转变形函数
rotate - 倾斜变形函数
skew - 矩阵变换变形函数
matrix
单个变形属性顺序
变形属性支持
transalate、sclae和rotate拆解属性,当使用单个变形属性时,效果总是按照先平移、后旋转、再缩放的顺序应用#mover { rotate: 30deg; scale: 1.5 1; translate: 10rem; } /* 相当于 */ #mover { transform: translate(10rem) rotate(30deg) scale(1.5 1); }- 平移变形函数
9.1.3 平移变形
平移变形只是沿着一个或多个轴的移动。例如,
translateX()沿着元素自身的X轴移动元素,translateY()沿着元素自身的Y轴移动元素,translateZ()沿着元素自身的z轴移动元素这些函数的值都是一个距离值,可以是长度,也可以是百分数,如果这个值是一个百分数,移动距离相对元素自身的尺寸计算
长度值的正负是基于笛卡尔坐标系的,也就是向右下前平移是正
如果想同时沿x轴和y轴平移一个元素,使用
translate()更简单。只要先提供x值,再提供y值,它的作用与你结合translateX()、translateY()一样,如果未提供第二个值,y值为0
translate函数示例 translateZ()将元素沿z轴平移,即在第三个维度中移动元素,与二维平移函数不同,函数只接受长度值,不允许使用百分数,实际上任何有关z轴的值都不可以使用百分数- 向z轴平移默认不会放大元素显示大小,需要结合旋转、视域转换等使用
translate()能同时设定x轴和y轴平移,类似地,translate3d()能同时设定x轴、y轴和z轴的平移量,但是如果translate3d()没有假定的默认值,也就是必须同时显示提供三个值
如果不希望使用变形属性,也可以使用平移属性来定义平移,默认值为none
translate: 150px -50px 100px; /* 等价于 */ transform: translate3d(150px, -50px, 100px);
9.1.4 缩放变形
缩放变形使一个元素放大或缩小,取决于你提供的值。这些值是无单位的实数,可以是正数也可以是负数,包括
scaleX(),scaleY(),scaleZ()函数提供给scale类函数的数字值是一个乘数
- 乘数支持倍数值:scaleX(2)将使元素的宽度是变形前的两倍,而scaleY(0.5)将把元素的高度缩小一半
- 百分数与数字值的比例为100:1:也就是说,50%的效果与0.5相同,200%与2相同,以此类推
如果想同时沿着两个轴进行缩放,可以使用
scale函数,依次接收x轴放大倍数和y轴放大倍数,如果不提供y轴放大倍数,默认为1不放大
scale函数示例 也可以在第三维度进行缩放,scaleZ()用于沿Z轴缩放,而scale3d()用于一次沿所有三个轴缩放
- 仅当元素有深度时,这两个函数才有效果,而元素在默认情况下是没有深度的,即元素最终显示平面要和xoy平面不平行
- 与translate3d()类似,scale3d()要求所有三个数字都是有效的。如若不然,无效的scale3d()将导致所属的整个变形值无效
实际上,缩放是修改了对应坐标轴上的点距离
与转换类似,可以使用
scale属性放大或缩小元素,而无需使用transform属性来执行此操作scale: 2 0.5 1; /* 等价于 */ transform: scale3d(2, 0.5, 1);
9.1.5 旋转变形
旋转函数使一个元素围绕一个轴或三维空间中的一个任意向量旋转。主要有四个简单的旋转函数,还有一个稍微复杂、专门针对三维空间的函数
四个简单旋转函数
rotate()、rotateX()、rotateY()、rotateZ()只接受一个值,即角度- 当没有任何形式的动画,超出一圈范围的值将与一圈内对应值效果相同,比如
397deg和一圈内的对应值37deg效果相同 - 如果一个值的数字超出了相应单位的常规范围,且有过渡动画,将转几圈后停在一圈范围内的对应位置
rotateZ()还有另外一个相同效果的函数rotate()

旋转函数示例 - 当没有任何形式的动画,超出一圈范围的值将与一圈内对应值效果相同,比如
此处的示例涉及3d效果,这里定义了视域变换属性
perspective,之后的涉及3d效果例子也是,具体属性效果见其他变形属性- 如果希望得到以特定向量指定了旋转轴正方向,并顺时针旋转特定角度,可以使用
rotate3d,共接收四个值,前三个值指定了三维空间中向量的x、y和z分量,第四个值是角度值,指定绕向量旋转的量之前的四个简单函数对应的旋转向量如下图

简单函数对应的旋转向量 rotate3d(1,1,0,45deg)并不等同于rotateX(45deg) rotateY(45deg) rotateZ(0deg),向量(1,1,0)为旋转轴正方向,最终旋转之后的效果如下,实际上相当于延一条对角线为轴顺时针旋转45deg
rotate3d旋转效果
与平移和缩放一样,也有一个旋转属性,允许你围绕各种轴旋转元素。然而,可取的值的语法有点不同
- 默认值none意味着不应用旋转
- 旋转函数和对应的
rotate属性值如下
/* 相当于rotate3d,也就是选择一个轴旋转 */ transform: rotate(-0.95, 0.5, 1, 45deg); /* 相当于 */ rotate: -0.95 0.5 1 45deg; /* 可以使用x y z (其中z可以省略) 代替对应轴向量 */ transform: rotateX(45deg); /* 相当于 */ rotate: x 45deg; transform: rotateY(33deg); /* 相当于 */ rotate: y 33deg; transform: rotateZ(-45deg); /* 相当于 */ rotate: z -45deg; transform: rotate(90deg); /* 相当于 */ rotate: 90deg;
9.1.6 倾斜变形
- 当你使用倾斜变形时,你可以将它沿着X轴和Y轴中的一个或两个轴进行倾斜。不存在Z轴或其他三维倾斜
skewX()、skewY()这两个函数使元素倾斜指定的角度,可以使用单个角度值作为输入相对于将坐标轴旋转一定的角度

倾斜变形例子 还有一个二维变形函数
skew(),支持输入一个或两个角度值skew(a,b)的效果与skewX(a) skewY(b)不同,前者通过矩阵运算[ax,by]实施二维倾斜,结果难以直接估计- 如果未提供第二个值,
b默认为0
目前没有倾斜属性,只能通过
transform属性添加倾斜变形
9.1.7 矩阵变换变形
使用
matrix()函数可以指定二维矩阵变形,也就是一个以六个值的变形矩阵的形式指定一个二维变形的函数- 一个有效的matrix()值是一个由六个以逗号分隔的数字。不能多,也不能少。这些值可以是正数或负数
- 该值描述了元素变形后的最终状态,可以涵盖其它所有变形类型(旋转、倾斜等)
- 经常由绘图或动画软件生成,当然也可以尝试计算
matrix()函数接受六个数字参数,matrix(a, b, c, d, e, f)对元素像素点坐标变换,对应齐次坐标变换矩阵为常用变换和对应的矩阵
仔细观察变换后的结果,可以发现
e和f分别表示坐标的偏移,如果在matrix(1, 0, 0, 1, e, f)的情况下,实际上就是translate(e, f),默认单位是px- 在
matrix(a, 0, 0, d, 0, 0)的情况下,a和d分别表示放大倍数,也就是scale(a, b) - 如果希望旋转
a角度,也就是rotate(a deg),需要matrix(cos(a), sin(a), -sin(a), cos(a), 0, 0) - 如果希望分别对x轴和y轴拉伸
a和b角度,也就是skew(a, b),需要matrix(1, tan(a), tan(b), 1, 0, 0)
还有一个三维矩阵变换函数
matrix3d(a1, b1, c1, d1, a2, b2, c2, d2, a3, b3, c3, d3, a4, b4, c4, d4),需要输入16个值,对应齐次坐标变换矩阵为目前没有矩阵变形属性,只能通过
transform属性添加矩阵变形
9.1.8 其他变形属性
移动变形的原点:默认情况下,元素的精准中心被用作变形原点,这是默认的行为,但是通过
transform-origin属性,可以改变它- transform-origin属性的值为两个或三个关键字,用于定义相对哪个点变形:
- 第一个值针对x轴,第二个值针对y轴,还可以选择一个沿z轴的长度
- 对于x轴和y轴,你可以使用普通的关键字,如
top和right,也可以使用百分数、长度,或不同类型的关键字组合 - 对于z轴,你不能使用纯英文的关键词或百分数,但可以使用任何长度值,其中像素值是目前最常用的
- 长度值被当作从元素的左上角开始的距离,百分数相对对应的轴和元素的尺寸计算
- transform-origin属性的值为两个或三个关键字,用于定义相对哪个点变形:
改变原点位置的计算方法:默认情况下,变形原点是相对于外边界
outer border计算的,不过可以通过transform-box属性改变,默认值是view-boxborder-box:使用元素的边框(外边框边缘)作为变形的参考框content-box:使用元素的内容框作为变形的参考框- 剩下三个是为
svg设计的,也可以在html中使用fill-box:使用元素的对象范围框(object bounding box)作为参考框stroke-box:使用元素的描边范围框(stroke bounding box)作为参考框view-box:使用该元素的最近的SVG视口作为参考框
选择3D风格:使用
transform-style可以设置在三维空间中还是平面中呈现元素,默认是平面flatflat:设置元素的子元素位于该元素的平面中preserve-3d:指示元素的子元素应位于3d空间中,子元素有可能被父元素遮挡
示例<div id="container"> outer <div class="inner">inner</div> </div> <select name="transform_style" id="style-selector" onchange="change_value()"> <option label="preserve-3d" value="preserve-3d" selected></option> <option label="flat" value="flat"></option> </select>function change_value(event) { const selector = document.getElementById("style-selector") var container = document.getElementById("container") container.style.transformStyle = selector.value }#container { width: 200px; height: 100px; border: brown 1px solid; position: relative; transform-style: preserve-3d; transform: perspective(700px) rotateY(60deg); font-size: 20px; margin-bottom: 30px; } .inner { position: relative; top: 20px; left: 30px; right: 10px; border: brown 1px solid; transform: translateZ(30px) rotateX(30deg); }强制使用flat的情况
某些属性的某些值要求元素及其子元素必须以扁平方式呈现(相同层叠上下文)才能起作用,在这种情况下,不管你把
transform-style设为什么值,都会被强制重置为flat。为了避免这种覆盖行为,请确保在任何有三维变形且有三维变形的子元素的容器元素上将以下属性设置为所列的值- overflow: visible
- filter: none
- clip: auto
- clip-path: none
- mask-image: none
- mask-border-source: none
- mix-blend-mode: normal
- isolation: auto
视域设置
perspective:如果你在三维空间中变形一个元素,有时希望能够基于某个特定视角查看,以得到不同的前后景深效果- 视域实际上是一个透视金字塔的造型,也就是从视域原点发散到z负半轴
- 属性接收一个绝对长度对应透视金字塔的长度
- 属性虽然不会继承,但会对所有子元素生效,元素和子元素会共享视域(视域原点是同一个点)
视域属性应该设置在父容器上,不然没有效果
perspective函数
perspective还有对应的视域变换函数perspective(),同样是接收一个绝对长度- 需要放入
transform属性中,应该将它排在第一位,或者至少排在依赖视域的变形之前 - 需要注意的是
perspective()函数只影响它所应用的元素,不会和其他元素共享视域

视域变换函数效果 视域原点
perspective-origin:相对父容器定义视角的起点,默认是50% 50%,也就是父容器的中间点- 支持两个长度输入对应离左上角坐标原点的x和y轴偏移,也支持通过
left、right、center等关键字定义 - 原点变化对3d元素是有影响的
和
perspective一样,属性应该在父容器添加
视域原点变化对元素的影响 - 支持两个长度输入对应离左上角坐标原点的x和y轴偏移,也支持通过
- 处理背面:3D转换让看见元素背面已经成为可能,甚至可能有意这么做。这样的情况如何处理,是由
backface-visibility属性决定的,这个属性相当简单。它所做的只是决定一个元素的背面是否在朝向观众时是否透过正面被渲染出来visible显示背面(初始值)hidden隐藏背面
只有原先的正面旋转到了背面时,元素的背面才有内容
9.2 过渡
过渡允许我们对CSS属性进行动画处理,使其随着时间的推移从一个原始值变成一个新的值。这些变化使一个元素从一个状态过渡到另一个状态,以响应某种变化。因此,我们可以使属性值逐渐变化,自然一些,不那么突兀
特别是快速的过渡,尤其是那些在大范围内移动或占据页面主要部分的过渡,有可能导致一些用户的癫痫发作。为了减少或消除这种风险,请使用
prefers-reduced-motion媒体查询限制过渡动画定义过渡需要定义初始状态,然后对以下内容进行定义
- 哪些变化需要触发过渡(明确列出哪些属性)
- 触发后多久开始过渡(延时)
- 过渡的持续时间(时长)
- 如何进行过渡动画(缓动函数)
应该避免对
auto值做过渡,因为对auto上使用动画,取决于浏览器及其版本,可能不会进行过渡,也可能会导致非预期结果支持动画的属性值
判断属性是否支持动画的关键是确定其取值能否插入中间的过渡值,也就是指在两个状态之间插入一个过渡状态。如果属性的计算值是关键字,不能过渡;如果关键字能计算为某种数值,则能过渡。简单而言,如果能找到属性的两个值的中间点,那么属性的值可能就支持动画
9.2.1 过渡属性
在
CSS中,过渡是用四个属性来定义的:transition-property,transition-duration,transition-timing-function和transition-delaytransition-property:指定哪个或哪些CSS属性用于过渡。只有指定的属性才会在过渡中发生动画,其他属性仍如通常那样瞬间变化- 默认值是
all,也就是过渡所有支持动画的属性 - 如果希望不过渡或取消所有过渡,使用属性值
none - 可以提供一个需要动画的属性列表作为属性值,并用
,分隔,支持使用all属性值,也就是说可以单独对部分属性设置其他过渡样式
如果想撤销部分属性的过渡效果,只能列出仍然想过渡的属性
- 默认值是
过渡事件
有四个与过渡有关的事件:
transitionstart、transitionrun、transitionend和transitioncancel- 这里主要讨论
transitionend,它可以被触发多次,例如,仅border-radius过渡就产生了四个transitionend事件,分别对应四个边框角的过渡,border产生八个事件,分别对应四边的边框宽度和颜色(边框样式不是过渡属性) - 过渡事件对象可获取到的属性
propertyName:这是进行、取消或完成过渡的CSS属性的名称pseudoElement:这是发生过渡的伪元素(含::),如果过渡效果应用到常规的DOM节点上,返回空字符串elapsedTime:事件经过的时间,如果是过渡取消(过渡还未发生的话为0)或终止事件为过渡持续的时间,如果是过渡执行和过渡开始事件为延迟的时间,以秒为单位
transition-duration:指定过渡的时长。你可以为所有属性指定一个值,或者指定多个值,或者为每个属性指定不同的时长- 支持单位为
s或ms的时间值,初始值为0s - 当在两个状态之间过渡时,如果为两个状态声明了不同持续时间,那么每个持续时间只在向那个状态过渡时起作用
- 属性支持输入多个以
,隔开的时间值,但不允许出现负值,如果有值不对,所有时间声明都无效 - 一般情况下,创造一个可见但不分散注意力的过渡的最佳时间范围是到毫秒
- 支持单位为
transition-timing-function:指定一个函数,定义属性值怎么变化。缓动函数定义属性如何计算。大多数缓动函数是线性、三次贝塞尔、阶跃函数,也可以从Easing Functions Cheat Sheet选择缓动效果下表中给出了此属性支持的值,其中线性函数和阶跃函数比较复杂
缓动函数或关键字 说明 ease慢速开始,然后加速,再慢下来,结束时特别慢 linear整个过渡过程保持相同的速度,即 linear(0, 1)ease-in慢速开始,然后加速 ease-out快速开始,然后减速 ease-in-out与 ease类似;中间较快,两端很慢,但不同速cubic-bezier(a,b,c,d)指定一个三次贝塞尔曲线 linear()指定一个线性函数 steps()指定一个阶跃函数 step-start表示缓动函数 steps(1, start)step-end表示缓动函数 steps(1, end)
函数使用细节
linear()支持一个以0开始1结尾的参数列表,其中的每一项都对应"y轴位置坐标 对应时间",也就是在对应时间到对应y轴的位置坐标- 对应时间应该提供0-1的数或百分比,也可以不给出,默认是线段的等分点

线性函数示例 cubic-bezier()三次贝塞尔曲线由P0、P1、P2和P3四点所定义。点P0和P3表示曲线的起止点,也就是起始状态定点。输入值为P1和P2的坐标,P2的y轴坐标可以大于1,达到回弹的效果
三次贝塞尔曲线示例 steps()需要两个参数,第一个参数是严格正的整数,表示构成阶跃函数的等距步数;第二个参数是以下关键字之一,图中的jump-start有等价关键字start,jump-end有等价关键字endjump-start:表示第一次跳跃在开始时发生jump-end:表示最后一次跳跃在结束时发生jump-none:表示两端均无跳跃,在中间跳跃jump-both:表示在0%和100%处均出现跳跃,相当于在插值过程中加上一步

阶跃函数示例
transition-delay:指定延迟,即属性开始变化时与过渡开始发生时之间的时长- 属性取值和
transition-duration基本相同,默认是0s,但延迟可设置为负数,过渡将立刻开始并且不触发transitionend事件 - 通常希望过渡同时开始,一般提供一个值
- 过渡不宜过长,一般取
100ms以内的值,防止过渡动画意外开始
- 属性取值和
不同长度的过渡值列表的对应关系
如果过渡属性的值列表长度不同:
- 以需要过渡的属性列表长度
n为准,其他属性值列表超过需要过渡的属性列表长度的部分将被舍弃 - 如果其他属性的值列表长度不够,则回到第一个列表项重复列表多次,直到得到长度
n的列表
简写属性
transition可以把上述四个过渡属性合在一起,属性可以被指定为一个或多个CSS属性的过渡效果,多个属性之间用逗号进行分隔- 默认顺序是
property,duration,timing-fuction,delay - 如果只提供一个时间,对应
duration,delay使用初始值0s - 每个值都可以省略,如果有值省略将使用初始值
/* 初始值 */ transition: all 0s ease 0s; transition: transform 200ms ease-in 50ms, opacity 200ms ease-in 50ms, /* 对应为 all 100ms ease 0s */ 100ms; transition: color 200ms ease, border-width 180ms ease-in 200ms, border-color 160ms ease-out 400ms, border-radius 140ms ease-in-out 600ms, opacity 100ms step-end 0.8s, width 2s steps(5, start) 1s, padding 3s steps(3, end) 1.2s;- 默认顺序是
9.2.2 其他细节
过渡是一种增强,虽然可以为早期浏览器添加测试版的过渡前缀,但一般没必要这么做
对于打印效果,打印的结果不会是中间过渡状态,如果没有单独定义打印的媒体查询规则,肯定是当前的页面实际的效果
如果一个过渡属性的取值中存在无法转换成数字的关键字,例如,如果
background-size不止有一个背景大小,存在从默认状态下的contain变为悬停状态的cover。那么,根据规范,整个列表不能插值。然而,有些浏览器为了过渡的目的会忽略了这一对特定的值,仍然对可过渡的值进行动画处理- 有些属性值是各种关键字,没有办法推断隐含的值(中间效果),这些属性不能过渡
- 有些属性值,如果浏览器可以推断出隐含的值,这些属性也支持动画
显示动画
由于
display和visibility属性不能过渡,所以不能使用过渡完成显示和隐藏,可以考虑以下方案- 使用透明度
opacity完成
渐隐渐显动画<div class="box"> </div>.box { width: 50px; height: 27px; background-color: aquamarine; border: #abc 1px solid; opacity: 1; transition: opacity .5s ease; cursor: pointer; &:hover { opacity: 0; } }- 使用
max-height完成
折叠显示动画<div class="menu-box"> <ul> <li>首页</li> <li>a</li> <li>b</li> </ul> </div>ul { list-style-type: none; padding-inline-start: 0; width: 50px; float: left; margin: 0; } li { padding: 0 5px; height: 25px; background-color: aquamarine; border: #abc 1px solid; } .menu-box { height: 27px; &:hover>ul { transition: max-height 4.5s ease; max-height: 100vh; } &>ul { overflow: hidden; max-height: 27px; } }- 使用透明度
如果属性不可过渡,添加到
transition列表中也没有关系,整个声明不会失败,浏览器将把不支持动画的属性剔除,过渡余下的属性数字过渡:如果属性要求是整数则以整数增减,不然以实数增减,通过
calc()完成颜色过渡:无论什么颜色都将转换为
RGBA色域空间进行过渡
9.3 动画
- 过渡也算是动画的一种,在过渡中,起始和终止状态是由元素的属性直接变化得到的,对时间和变化方式没有更多的主动权,而本节的动画改变的属性值可以不在元素前后的两个状态之间,也就是引入新的中间状态,在
CSS中称为关键帧,实际上也类似flash关键帧和过渡一样,为了避免癫痫患者发病或不适,应该用
@media prefers-reduced-motion查询进行限制 - 动画的性能:有些动画在所有浏览器中都在
UI线程中播放,这可能会造成视觉效果卡顿,但在大部分浏览器中,透明度和变形动画都是通过GPU渲染的,无需等UI线程空闲,反而如果将变形动画放入非动画中,会使用UI线程- 对于大量
3d变形,UI线程需要处理将元素放入不同的层中,每个单独的层都要消耗GPU视频内存,在之后UI线程将元素放入GPU合成的层中。创建的层越多,性能损耗越大,因此,最好将变形和透明度添加到动画中完成 - 而其他涉及元素重排的属性放入变形则可能对性能也不利,不要一股脑都交给
GPU
- 对于大量
9.3.1 定义关键帧
若想为元素添加动画效果,就要有关键帧,而这又要求有一个具名动画。首先要使用
@keyframes规则定义一个可复用的CSS关键帧动画,并取一个名字,然后通过名称将动画应用到元素或伪元素上一个@keyframes规则有一个动画标识符(即动画的名称),以及一到多个关键帧块
动画标识符可以使用
-,_,字母数字和Unicode中\u00A0到\u10FFFF的字符,但不能以数字开头或使用-,_开头后第二个字符是数字每个关键帧块有一到多个关键帧选择符,声明属性及其值,关键帧选择通过动画持续时间内的时间点百分比或关键字
from以及to完成,from对应0%,to对应100%@keyframs fadeout { from { opacity: 1; } to { opacity: 0; } }如果有关键帧的内容是相同的,可以通过
,分隔,比如25%, 75%,当然也可以定义重复的关键帧标识,后面定义关键帧中的属性将覆盖前面的关键帧的属性@keyframes w { from, to { left: 0; top: 0; } 25%, 75%, to { top: 100%; } 50% { top: 50%; } }开始和结尾关键帧都可以省略,如果省略将使用元素本身预定义的属性作为开始或结尾关键帧的属性值,或是使用时的动画列表,位于此动画前面的动画中的开始和结束关键帧中对应属性值
@keyframes change-color { 50% { color: orange; } }整个
@keyframes规则设置一个动画效果的完整迭代过程,并没有涉及动画时间,如果通过时间值进行关键帧选择,用户代理将忽略这一帧动画可以迭代零次或多次,这主要取决于
animation-teration-count属性的值
通过JavaScript脚本处理动画
js可以处理动画,获取动画帧信息或添加删除额外的属性,为了获取和修改动画,需要先通过document.styleSheets[i].cssRules[j]找到第i个css style块的第j条声明var a = document.styleSheets[0].cssRules[1] // 寻找到对应关键帧,要求关键帧的选择符个数和顺序完全一致,返回最后一个对应上的关键帧 // 如果对应上就返回一个CSSKeyframeRule对象,否则返回null // 此对象有两个值,对 from,to {color: black;} 关键帧有以下值 // cssText:"0%, 100% { color: black; }" // keyText:"0%, 100%" var rule = a.findRule("from,to") // 在最后添加帧 a.appendRule("40% {color: red;}") // 删除最后一个对应的帧 a.deleteRule("40%")
9.3.2 把动画应用到元素上
为了把动画附加到元素上,CSS提供了多个相关的属性,若想要展示动画,至少要设置动画的名称和持续时间
指定动画的名称
animation-name属性的值为,分隔的列表指定想引用的动画名称如果列表中有动画不存在,对应动画名称将被忽略
如果列表中动画中有重复的属性,后面动画的属性将覆盖前面的动画中的属性值
如果动画列表中有一个动画没有定义开始和结尾关键帧
- 之前有包含对应属性的开始和结尾关键帧,计算值是前一个动画中的对应开始和结尾关键帧对应属性的值
- 如果没有包含对应属性的开始和结尾关键帧,未动画时的默认值
- 开始和结尾关键帧是分开判断的
div { --bgcolor: red; animation: infinite 10s alternate; /* bg-shift没有开始和结尾关键帧,向前找到对应属性的开始关键帧,会使用对应关键帧 */ /* change_bgcolor动画除了开始关键帧,其他关键帧中的背景属性不会被应用 */ /* change_bgcolor中的边框尺寸属性未被覆盖,会生效 */ animation-name: change_bgcolor, bg-shift; background-color: var(--bgcolor); } @keyframes bg-shift { 35% { background-color: orange; } 55% { background-color: red; } 65% { background-color: purple; } } @keyframes change_bgcolor { 0% { background-color: yellow; } 45% { background-color: green; border-width: 10px; } 55% { background-color: blue; border-width: 20px; } }
设置动画的时长
animation-duration,使用方法和transition-duration一样,支持时间列表输入,对应动画名称列表- 支持单位为
s或ms的时间值,初始值为0s - 属性支持输入多个以
,隔开的时间值,但不允许出现负值 - 如果有值不对,所有时间声明都无效
- 支持单位为
9.3.3 其他动画属性
设置动画的重复次数
animation-iteration-count,可以使用数字值或infinite表示重复多少次- 默认迭代一次,取值为1
- 支持非整数,将在最后一次重复中途停止
infinite表示无限重复- 不允许使用负值,如果提供的值无效将使用默认值
- 可以提供次数列表,对应动画名称列表
- 通过设置可以让不同持续时间的动画同时结束
.flag { animate-name: red, white, blue; animatie-duration: 6s, 4s, 2s; animate-iteration-count: 2, 3, 6; }设置动画的延迟时间
animation-delay,支持时间列表输入- 如果设置负的延迟时间,将从动画中间立即开始播放,具体是哪个状态和缓动函数有关
- 可以通过延迟时间设置动画链,让列表中的动画依次连续播放,当然动画链也可以通过
js监听动画结束事件修改动画的名称实现
css实现.rainbow { animation-name: red, orange, yellow, blue, green; animation-duration: 1s, 3s, 5s, 7s, 11s; animation-delay: 3s, 4s, 7s, 12s, 19s; }js实现let iteration = 0; const animationNameList = ["red", "orange", "yellow", "blue", "green"]; const el = document.getElementById('element_id'); el.addEventListener('animationiteration', () => { el.style.animationName = animationNameList[iteration++]; });- 没有定义每次循环之间的延迟属性,但可以进行伪造,也就是多次应用一个动画,通过动画链设置循环中间延迟时间。当然也可以通过
js监听动画结束事件,在结束时移除包含所有动画相关属性的类,然后setInterval延迟一段时间再添加上这个类实现时间控制
css实现.rainbow { animation-name: bg_change, bg_change, bg_change; animation-duration: 1s; animation-delay: 3s, 5s, 7s; } /* 也可以通过重复的动画关键帧实现,但十分麻烦 */ @keyframes color_and_scale_3_times { 0%, 13.32%, 20.01%, 40%, 46.67%, 93.32% { transform: scale(1); background-color: red; } 13.33%, 40.01%, 93.33% { background-color: green; transform: scale(0.5); } 20%, 46.66%, 100% { background-color: yellow; transform: scale(1.5); } }js实现const el = document.getElementById('element_id'); el.addEventListener('animationend', () => { el.classList.remove('animationClass'); setTimeout(() => { el.classList.add('animationClass'); }, 1000); });对事件触发的影响
目前介绍的属性对动画事件的触发有不同的影响
- 如果有正数延迟,在延迟结束后触发
animationstart事件,其他情况都是立即触发 - 动画结束事件
animationend必须在动画结束的时候触发,如果设置无限重复,且持续时间大于0,animationend事件不会触发 - 比过渡新增的事件
animationiteration在两次过渡重复之间触发,必须持续时间大于0的中间动画结束才会触发,最后一次动画不触发。如果由于负数延迟略过了前几次动画,前几次的animationiteration也不会触发
设置动画方向
animation-direction,可以定义按什么方向播放动画关键帧normal:动画在每个循环中正向播放。换句话说,每次动画循环时,动画将重置为起始状态并重新开始。这是默认值reverse:动画在每个循环中反向播放。换句话说,每次动画循环时,动画将重置为结束状态并重新开始。动画步骤将反向执行,并且时间函数也将被反转alternate:动画在每个循环中正反交替播放,第一次循环是正向播放alternate-reverse:动画在每个循环中正反交替播放,第一次循环是反向播放
设置动画的缓动函数
animation-timing-function作用和transition-timing-fuction一样,设置动画的过渡步调,接收一个缓动函数或关键字,取值详见过渡部分的缓动函数介绍- 需要注意的是,虽然缓动函数值本身好像没有办法过渡,但这个属性可以添加到动画帧中,指定到下一个关键帧时使用的缓动函数
设置动画的播放状态
animation-play-state,用于设置动画是播放还是暂停的,有两个取值running播放和pause暂停- 如果在延迟时间内,暂停也是有效的,会暂停延迟,在播放时继续剩下的延迟时间
- 恢复暂停的动画将从暂停时停止的位置开始播放,而不是从动画序列的开头重新开始播放
- 可以配合
hover等用户操作伪类和脚本使用
设置动画执行前后元素的状态
animation-fill-mode,也就是设置在执行前后是否应用第一个和最后一个关键帧中的属性值,有下列取值none:当动画未执行时,动画将不会将任何样式应用于目标,而是已经赋予给该元素的 CSS 规则来显示该元素。这是默认值forwards:目标将保留由执行期间遇到的最后一个关键帧计算值backwards:动画将在应用于目标时立即应用第一个关键帧中定义的值,并在延迟时保持both:动画将遵循forwards和backwards的规则
这些属性可以合在一起写成
animation属性animation-nameanimation-durationanimation-timing-functionanimation-delayanimation-iteration-countanimation-directionanimation-fill-modeanimation-play-state
animation- 使用时属性基本上都可以省略,省略的属性将用默认值
- 如果有一个时间将被赋值给持续时间,第二个时间被解释为延迟
- 如果动画名称和其他属性关键字相同(不建议这么做),需要声明对应属性,并在末尾声明动画名称,
none可能是唯一不能用于动画名称的关键字 - 每个动画用
,隔开
.snowflake { animation: 3s ease-in 200ms 32 forwards falling, 1.5s linear 200ms 64 spinning; }
9.3.4 其他细节
动画定义的属性优先级比普通的样式属性高,但比
!important样式低,会发生层叠display: none;对动画有影响,将中断动画,像是把动画分离了一样,当display变为某个可见值后,将重新动画动画在
UI线程中的优先级最低,如果延迟之后,UI线程没有空闲,不会进行动画可以在元素上添加
will-change属性告诉浏览器哪些元素接下来要改变,以便浏览器优化- 这应该作为优化的最后手段,并非用于预测性能问题
- 不要将
will-change应用于过多的元素,因为优化会关联额外的资源,带来其他的问题(页面变慢和大量资源消耗) - 应该在不需要改变之后移除该属性,浏览器进行的优化通常将在尽可能短的时间内删除并恢复到正常状态
- 不要为了过早优化而将
will-change应用于元素,如果页面性能足够好不应该使用此属性 - 要给它足够的时间来发挥作用
- 当与创建层叠上下文的属性一起使用时,可能影响显示
- 取值
auto不告诉接下来的变化类型,浏览器将进行猜测scroll-position表示开发者希望在不久后改变滚动条的位置或者使之产生动画contents表示开发者希望在不久后改变元素内容中的某些东西,或者使它们产生动画- 其他可动画属性,比如
left,表示开发者期望在不久的将来对元素上给定名称的属性进行动画或更改
对于动画最好通过
prefer-reduce-motion媒体查询限制@media (prefer-reduce-motion) { * { anmation: none !important; transition: none !important; } }打印动画将固定元素当前的状态,如果动画将属性变成
color: red;,打印出来字体就是红色
