🎉 一、前言
css3的animation
想必大家都知道吧,那 steps
逐帧动画你知道吗?对于我来说,实际工作及练习中也很少用到这种跳跃式变化的动画,而它start
和end
的解释又比较“不说人话”,以前用到steps
动画的时候,常常是靠调试,来回设置start
和end
,主打的就是瞎猫碰上死耗子。虽然之前也看过关于他们区别的文章,但都是半知半解,过两天就剩零知零解了。最近忙里偷闲,我终于打算一探究竟了,我倒要看看start
和end
到底有什么区别!😠😠😠 顺便写几个小demo造福一方~
🔥 二、逐帧动画介绍
animation的工作原理是通过将元素的CSS样式从一个状态改变为另一个状态时(我们称为线性变化),浏览器会在每个关键帧之间插入补间动画,所以动画效果是连贯性的,这也就是我们常用的 补间动画
。
而steps()
逐帧动画则是跳跃式变化,如果说补间动画
是一个滑坡式的变化,那么逐帧动画
就是阶梯式变化,它的变化没有中间过程。补间动画
就像你看的普通动画片,而逐帧动画
就像是那种定格动画。
语法:
animation-timing-function: steps(number, [end | start])
参数说明:
-
number
参数指定了时间函数中的间隔数量(必须是正整数) -
第二个参数是可选的,可设值:
start
和end
,表示在每个间隔的起点或是终点发生阶跃变化,如果忽略,默认是end。
🌈 三、图解图解 step-start
和 step-end
区别
什么叫在间隔的起点或终点发生变化呢?光看文字十有八九看不懂,下面就用示例代码来说明。
上图是我ps的一张图,尺寸为200*750,共5个色块,每个色块高度150。
在示例代码中我将以这张图为背景,每一帧将背景上升一个色块的高度。关键代码如下:
animation: ani 5s 2s steps(5,start) infinite backwards;
@keyframes ani{
100%{
background-position:0px -750px;
}
}
🍀 在设置动画前的初始状态:
🍀 再直接来看看动画末态的情况:
一个色块150px,所以动画末态是背景图片向上移动750px。
为了完整的看到动画效果,我设置了2秒的动画延迟
🍀 我们设置的steps的第一个参数number
为 5
,也就是把整个动画过程切割成5个片段,如下图:
在实验之前先来分析一下,既然是片段,那必然有片段的起点和终点,可以把补间动画看作点,而逐帧动画则是面。那么这五个片段的起点终点是哪呢,如下图:
你会发现,动画是由6个点切成段五段,带着这个思路开始下面的实验。
🍀 先来看一下设置 start
的效果:
你会发现色块1怎么不显示了,甚至在动画没开始前,也就是延时阶段直接就显示了【2】,变化过程为: 2 - 3 - 4 - 5 - 空
分析一下就可以想到,start
是在间隔的起点发生阶越变化,即开始直接就发生变化了,第一段直接阶越到了第一段结束的位置。
🍀 再来看下设置 end
的效果:
你发现动画变正常了,动画过程是从【1】到【5】。
再分析一下,因为end是在间隔终点发生阶越变化,即每一段都会在其开始阶段进行停留,这一段结束后才会发生变化直接阶越到下一段的开始状态。
🍀 总结:
可以将补间动画和 steps
逐帧动画类比于点和线
的区别,steps切割开的每个动画片段就是一条样式不变的线,而线都有首尾两个点。
设置 start
的 steps
的动画总是在开始发生变化,即逐帧显示每一段的终点;
而设置 end
的 steps
的动画总是在结束发生变化,即逐帧显示每一段的起点;
其实很简单的道理,为什么总是记不住呢,因为他和人的惯性思维恰好相反。设置start
总觉得是显示每一段的开头,可它恰好相反,start
是开头发生变化,显示的都是每一段的结尾。
🍀 另一种理解思路:
steps(number, [end | start])
是将动画分为number
段,共有number + 1
帧画面。start
就是抛弃第一帧画面执行动画,end
就是抛弃最后一帧画面执行动画。
注意: 第二个参数还有两个内置值,step-start
等同于steps(1,start)
,动画分成1步,2个节点,抛弃第一个节点,即显示结尾节点的状态;同理step-end
等同于steps(1,end)
。
jump-start:在每个时间间隔开始的时候跳1步到下一状态位置;
jump-end:在每个时间间隔结束的时候跳1步到下一状态位置;
jump-both:在每个时间间隔开始和结束的时候跳1步到下一状态位置,跳步次数会比预设的多一次;
jump-none:在每个状态位置停留够一个时间间隔才跳到下一位置,跳步次数会比与预设的少一次
🔑 四、思考
上面我只设置了动画100%时的状态,那如果我设置了多个关键帧的状态呢,那还是以整个动画过程切割成number
段吗?
我们再来做几个实验:
实验1:
我们将动画时间由5秒改成10秒(为了方便观察,我们设置steps第二个参数为end,放弃第一帧画面),然后将原先的动画末态改到50%,并在动画100%时增加边框。·
animation: ani 10s 2s steps(5,end) infinite backwards;
@keyframes ani{
50%{
background-position:0px -750px;
}
100%{
border: 100px solid red;
}
}
结果如下图:
观察后发现,在10秒的完整动画期间:background-position
的变化过程是图像显示由1到5,再由5到1,共变化了 【10】 次,而我设置的steps的number
参数是 【5】,这就打破了上面我说的以整个动画过程切割成number
段的假说。
同时可以观察到,border
的变化过程共进行了5次,因为我们只在100%的时候设置了border
。
得出结论:
steps
的number
参数并不是将整个动画过程切割成number
段,而是对于某个css样式来说,每一段关键帧的变化切割成number
段。
实验2:
假想:上面我们只在动画100%的时候设置了100px的boder
,如果我们在50%的时候也设置border
,并且状态恰好是100%的一半,这样对于动画0%到100%是一个流畅的线性变化。请问这时候动画还会被切成5段吗?
得出结论: 即使将几个关键帧的css变化设置的具有规律性,但是
steps
仍然会将每段关键帧的变化切割成number
段,即只要在这个关键帧里设置了某个css,那么对于这个css来说,这个关键帧就会被视为steps
动画的端点。
实验3:
那既然每段关键帧都会被steps
切割成number
段,那每段的steps
动画执行的时间怎么划分呢?其实想想就能想到,应该是按照关键帧占整个动画过程的比例分割整个动画时间。
如下图设置boder
:【0%-50%】宽度由0到100,【50%-75%】宽度由100到0,【75%-100%】宽度由0到100
很明显可以观察到,border宽度变化的时间为 2:1:1,即验证了我上面的推论。
⚔ 五、steps() 动画实践
下面我举几个steps() 动画的使用场景。
1. 动物或人物的动作变化–铁山靠
用一张人物动作关键帧的长图,和上面的案例一样,通过修改背景图片位置,实现动物或人物的动作变化。作为一名蒸爱粉,我给哥哥做了一个跳舞的动画:
jcode
2. 打字机效果–“只因你太美”
打字机的原理是用一个和文字总宽度一样的div覆盖文字,并用这个div的边框
设置steps()动画实现光标效果,然后减小div宽度(每一帧减小一个文字的宽度),让下面文字漏出来就好了~\
点击运行查看效果~
jcode
3. 纯css实现倒计时
我这里提供了两种实现方案,准确来说是三种:
方案1: var() css变量 + counter-reset
计数器 + @property
规则 + steps()逐帧动画
使用css变量和counter-reset
计数器来实现倒计时的数字,只要设置动画,在5秒内将变量由5变为0即可实现倒计时,但是变量的变化是不会被浏览器添加补间动画
的,即只会在5秒后直接变成0,而不会有中间,5-4-3-2-1-0的过程,这时我们再利用@property
关键字为这个变量配置规则,实现数字变化的动态过程!
而最后出现的 “Go” 可以利用step-end
逐帧动画,在5秒后将文字修改成 “Go”,或者利用@counter-style
关键字自定义计数器规则,在变量变化到0的时候,定义一个symbols
符号。
如果你不了解
counter-reset
、@property
和@counter-style
,可以查看以下两篇文章:
CSS counter-reset 属性
mdn 关于@property API 说明
mdn 关于@counter-style 说明
点击运行查看效果~
jcode
方案2: 只用steps()逐帧动画
其实这个就很简单了,所有的数字和最后的 “GO” 都在html里写死并设置等高,然后就可以向上面移动图片位置一样移动这些数字进行显示了。
点击运行查看效果~
jcode
4. 其他应用场景
平常工作中可以用到steps()
逐帧动画的场景也有很多:
- 例如在延迟n秒后修改元素某个样式,常规可能需要用js写个延时器动态修改它的css,这完全可以用
step-start
动画代替; - 再比如除了上面那种大屏的倒计时,普通的时分秒倒计时也可以用
steps
代替js实现,实现原理也简单,就是将 0-9 制作成一张精灵图,然后时分秒每个单位都各自用这个精灵图当作背景,例如对于秒的数字,可以设置动画时长为10s,动画函数为steps(10,start)
,这样每次数字变化都是 1s,同理对于分的数字,设置动画时长为 600s,动画函数为steps(10,start)
,这样每次数字变化都是 60s。
💥 六、写在最后
我是喜欢归纳总结前端相关知识的前端阿彬,尽力持续输出原创优质文章,欢迎点赞关注😘
往期文章:
# 🕸2023 前端 SEO 无死角解读
# 我给自己搭建的前端导航网站,你们都别用🤪
# 2023 最新最细 vite+vue3+ts 多页面项目架构,建议收藏备用!
# 浅谈 强制缓存/协商缓存 怎么用?
# 2023 前端性能优化清单