animation

2023-03-15 18:49:58 浏览数 (2)

一.CSS动画

CSS动画相对JS动画有2个主要优势:

1.流畅

因为渲染引擎可以通过跳帧(frame-skipping)及其它技术来确保性能尽量流畅

2.浏览器性能优化

把动画序列交给浏览器去控制,这样浏览器就能优化性能和效率,比如对于看不见的tab,可以减少刷新频率

定义动画分为2部分

  • 配置animation各项子属性
  • 通过@keyframes定义关键帧样式

浏览器根据这些东西来创建补间动画,计算插值把各个关键帧连接起来

二.animation子属性

代码语言:javascript复制
animation-name @keyframes定义的关键帧名,默认none
animation-duration 动画时长,默认0s,与transition完全一致
animation-timing-function easying函数,默认ease,与transition完全一致
animation-delay 延迟时间,默认0s,,与transition完全一致
animation-iteration-count 重复次数,默认1
animation-direction 方向,默认normal
animation-fill-mode 样式应用模式,默认none
animation-play-state 用来暂停/恢复动画序列,默认running

需要注意:

  • durationdelay,其它参数顺序随意
  • animation-name不要和关键字重名,会优先匹配属性

animation-iteration-count

代码语言:javascript复制
animation-iteration-count = infinite | <number>

动画重复次数,各值分别表示无限次、指定次数

animation-direction

代码语言:javascript复制
animation-direction = normal | reverse | alternate | alternate-reverse

动画执行方向,各值分别表示正向、反向、交替(奇数次正向偶数次反向)、反向交替(奇数次反向偶数次正向)

animation-fill-mode

代码语言:javascript复制
animation-fill-mode = none | forwards | backwards | both

样式应用模式,各值分别表示不应用关键帧样式、(结束后)应用终态样式、(delay期间)应用初态样式、(delay期间)应用初态样式且(结束后)应用终态样式

注意:初态和终态可能是0%也可能是100%,由animation-iteration-countanimation-direction共同决定

关键字含义如下:

代码语言:javascript复制
none 在动画结束后,去掉@keyframes定义的样式,恢复原样式
forwards 在动画结束后,保持终态样式
backwards 在动画开始前(delay期间),保持初态样式
both 同时具有forwards和backwards的效果,即在delay期间保持初态样式,在动画结束后保持终态样式

具体差异见Demo:http://www.ayqy.net/temp/animation/animation-fill-mode.html,点击红色块开始动画

animation-play-state

代码语言:javascript复制
animation-play-state = running | paused

决定动画执行还是暂停,可以用来控制动画暂停/恢复,比delay更强大更灵活一些

具体效果见Demo:http://www.ayqy.net/temp/animation/animation-play-state.html

三.@keyframes

语法如下:

代码语言:javascript复制
@keyframes anmiationName {
   0% {}
   /*...*/
   100% {}
}

如果没有定义0%100%,浏览器就根据其它时刻的关键帧给所有属性算一组值

P.S.tofrom分别是0%100%的别名,因为初态和终态比较重要,有权申请英文名

四.事件

transition只有一个end事件,animation提供了3个事件:

代码语言:javascript复制
animationstart      开始
animationend        结束
animationiteration  开始下一次重复

事件对象有3个特殊属性:

  • animationName 即animation-name
  • elapsedTime 单位是秒,对于animationstartanimationend表示动画执行到此刻的时间,对于animationiteration,表示下一次重复开始的时间,与transitionend事件类似,一般不受delay影响 特殊的,animationstart中的elapsedTime一般为0,除非animation-delay是个负值,此时elapsedTime-1 * delay
  • pseudoElement 以::开头的伪元素名,如果动画不是应用在伪元素上,就是空串

注意:最后一次重复结束的时候,不会触发animationiteration,而是触发animationend

五.技巧

1.steps(1)去掉平滑过渡

steps(1)linear很像,去掉一个linear动画的补间过渡,只留下关键帧,关键帧之间的帧延续上一个关键帧,就得到了steps(1)

制作Flash时,先插入两个关键帧,此时两个关键帧之间的都是普通帧(用来延长上一个关键帧的播放时间),这时的效果就是steps(1)。右键后一个关键帧,创建补间动画,此时得到的就是linear效果

实例如下:

代码语言:javascript复制
.rgb {
   -webkit-animation: rgb 1.5s linear infinite;
   animation: rgb 1.5s linear infinite;
}
@keyframes rgb {
   0% {
       opacity: red;
   }
   33% {
       background: green;
   }
   66% {
       background: blue;
   }
}

效果是背景色红绿蓝平滑渐变,想去掉渐变的平滑过渡,直接把linear改为steps(1)即可,如下:

代码语言:javascript复制
.rgb-step {
   -webkit-animation: rgb 1.5s steps(1) infinite;
   animation: rgb 1.5s steps(1) infinite;
}

效果就变成了每0.5秒切换一次背景色,没有渐变过渡

具体应用:两种状态无限切换(闪烁)

代码语言:javascript复制
.blink {
   -webkit-animation: blink 1s steps(1) infinite;
   animation: blink 1s steps(1) infinite;
}
@keyframes blink {
   0% {
       opacity: 0;
   }
   50% {
       opacity: 1;
   }
}

2.添加关键帧去掉平滑过渡

闪烁效果有另一种有趣的实现方式:

代码语言:javascript复制
.blink {
   -webkit-animation: blink 1s linear infinite;
   animation: blink 1s linear infinite;
}
@keyframes blink {
   0% {
       opacity: 0;
   }
   50% {
       opacity: 0;
   }
   50.01% {
       opacity: 1;
   }
   100% {
       opacity: 1;
   }
}

虽然还是linear平滑过渡,但插入的:

代码语言:javascript复制
50.01% {
   opacity: 1;
}

去掉了50% -> 100%的补间,把透明度补间转移到50% -> 50.01%,时间较短的情况下,这个补间变化不会被察觉,当然,如果时间足够长,比如:

代码语言:javascript复制
.blink {
   -webkit-animation: blink 10000s linear infinite;
   animation: blink 10000s linear infinite;
}

就应该能看到透明度在某1秒内从0渐变到1,但一般情况下,这样实现闪烁在效果上是没有问题的

3.关键帧控制延迟

animation-delay只在动画开始前有效,每次重复不会插入延迟。类似于上面50.01%的技巧,可以通过插入空白关键帧来给每次重复插入延迟,实现loading转一圈等一等的效果:

代码语言:javascript复制
.wait {
   -webkit-animation: wait 1s linear infinite;
   animation: wait 1s linear infinite;
}
@keyframes wait {
   0% {
       -webkit-transform: rotateZ(0);
       transform: rotateZ(0);
   }
   40% {
       -webkit-transform: rotateZ(0);
       transform: rotateZ(0);
   }
   100% {
       -webkit-transform: rotateZ(360deg);
       transform: rotateZ(360deg);
   }
}

做到了每转一圈等0.4s

4.steps逐帧动画

把序列帧平铺在一张图片上,修改background-position

steps()实现的话需要在末尾复制第一帧(比如6帧动画,需要7帧平铺图),例如:

代码语言:javascript复制
.walk {
   background: url(walk.svg);
   width: 162px;
   height: 230px;   -webkit-animation: walk 1s steps(22) infinite;
   animation: walk 1s steps(22) infinite;
}
@keyframes walk {
   0% {
       background-position: 0 0;
   }
   100% {
       background-position: -3564px 0;
   }
}

其中walk.svg横向铺满了23帧,每帧尺寸是162 * 230,向左抽背景图片,最多能抽162 * 22 = 3564,此时显示最后一帧(图片帧内容与第一帧相同),首尾接起来

当然,还有另一种方法,用steps(1)去掉平滑过渡,然后手动设置22个关键帧,比较费劲,这里不给例子,但肯定是可行的

在线Demo:http://www.ayqy.net/temp/animation/css-animation-tricks.html

六.总结

CSS animation的定义方式和Flash非常相似,比如Flash中的几个概念:

  • 关键帧:如果你希望某处的内容要跟前面不一样,就插入关键帧
  • 空白关键帧:表示上面没内容,以小白点显示。他可以跟关键帧互相转换,放了内容就成关键帧了。关键帧上的内容去掉就是空白关键帧
  • 普通帧:关键帧或者空白关键帧后面延续的是普通帧。普通帧是延续之前关键帧的内容,所以他的作用可以来控制动画的显示时间

对应到CSS的@keyframes定义中感受一下,是不是有点意思?

参考资料

  • Using CSS animations – CSS | MDN

0 人点赞