之前在这篇文章中:CSS 实现 Ant Design官网Logo彩蛋效果实现了一个鼠标 hover 效果,如下
原理其实很简单,就是一个 CSS 动画,使用的是steps
阶梯函数,不断改变background-position
.logo{
animation: random 1s steps(10) infinite;
}
@keyframes random {
to {
background-position: 100%;
}
}
里面用到的小图标是这样一张图片(11个小图标)
乍一看,动画好像非常完美,其实还是有一个小小的缺陷,仔细观察,最后一个图标(点赞图标)一直没有出现过,直接被跳过了,文章评论中也有人提到并给出解决方案
但是这种方式不太稳定,background-potion
需要考虑实际的帧数,也就是需要根据steps
的步数改变。
除了这种方式,其实还有更好的解决方案,今天就一起来探讨这个问题
一、问题重现
为了方便观察和演示,这里用1、2、3
来做一张序列帧图片,如下
然后通过 steps
阶梯函数实现序列帧动画,关键实现如下
div{
animation: random 1s steps(2) infinite;
}
@keyframes random {
to {
background-position: 100%;
}
}
效果是这样的(丢失了最后一帧)
为啥这里明明有 3 帧,却要设置steps
的次数为 2 ?我们不妨设置为 3 试试
animation: random 1s steps(3) infinite;
效果如下
情况就更加糟糕了,而且并不是按照1
、2
、3
的边界变化的,还出现了中间过渡状态,显然不是我们想要的效果。
要搞清楚这个问题,需要理解steps
函数中的第 2 个参数,继续往下看。
二、 steps 中的首尾帧忽略规则
steps
其实有两个参数,官方语法如下
steps( [, ]? )
现在再来看前面的写法
steps(2)
这其实是一个简写,等同于
steps(2, end)
这是什么意思呢,end
表示结束,也就是忽略最后一帧,所以上面的例子中,虽然有 3 帧,但忽略最后一帧后就只有 2 帧了,如下
在动画中就是这样,1→2→1→2...
所以,steps(2)
表示将原图片分成 3 帧,但是只运行前面 2 帧,最后一帧跳过。
那么,上面的steps(3)
也很好理解了吧,将原图片平均分成了 4 份,所以就出现了中间过渡状态
除了end
以外,还有一个start
steps(2, start)
start
表示开始,也就是忽略最前面的一帧
在动画中就是这样,2→3→2→3...
所以,steps(2,start)
表示将原图片分成 3 帧,但是只运行后面 2 帧,跳过了第一帧。
那么,有没有办法分成多少帧就运行多少帧呢?当然也是有的,就是后来更新的jump-*
关键词
三、steps 中jumb-*关键词
从 Chrome 77+
开始,steps
支持了几个以jumb-
开头的关键词,分别是
- jump-start,等同于之前的
start
,表示跳过第一帧 - jump-end,等同于之前的
end
,表示跳过最后一帧 - jump-both,表示跳过第一帧和最后一帧
- jump-none,表示都不跳过
下面是官方的一个函数图像(每个实心点表示一帧)
其实我还是比较习惯于用跳过来理解,前面两个就不说了,先看jumb-both
steps(1, jump-both)
去除首尾两帧后,就只剩下中间一帧了,示意如下
最后的动画效果也只有中间一帧了,2→2→2→2...
然后是jump-none
steps(3, jump-none)
表示不跳过,有多少帧就运行多少帧
动画效果就是正常的1
、2
、3
依次变化,1→2→3→1→2→3...
根据这个原理,文章开头的小缺陷就非常好解决了,将steps(10)
改为steps(11, jump-none)
即可
.logo{
animation: random 1s steps(11, jump-none) infinite;
}
@keyframes random {
to {
background-position: 100%;
}
}
这样就可以正常的看到最后一个图标了
你也可以访问以下任意链接:
之前的效果(注意对比观察最后一个点赞图标)
四、最后总结一下
其实这个特性也已经出来好几年了,但是好像很少有人知道,主要原因是这个特性使用场景不太丰富,并且有替代方案,再者,这个特性是旧属性的补充,导致在文档上不易轻易被发现(我也是无意发现的)。兼容性方面,Safari
还是有些拉胯,需要14+
才行,完整兼容性如下:
下面再来回顾一下jump-*
的区别
jump-start
,等同于之前的start
,表示跳过第一帧jump-end
,等同于之前的end
,表示跳过最后一帧jump-both
,表示跳过第一帧和最后一帧jump-none
,表示都不跳过,有多少帧就运行多少帧
最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发❤❤❤