这篇文章发布于 2018年06月11日,星期一,23:49,归类于 CSS相关。 阅读 71513 次, 今日 15 次 18 条评论
by zhangxinxu from http://www.zhangxinxu.com/wordpress/?p=7618
本文可全文转载,但需要保留原作者和出处,摘要引流则随意。
一、不只是连续的变化才叫动画
我很早的时候有个错误认知,认为只有连续的变化才叫动画;含笑半步癫那样的效果不是动画。
后来才明白,只要有轨迹可循,即使肉眼看上去是断断续续的,实际上也是动画。
举个例子,现有素材图如下:
请问下面两个图形变化,哪个属于动画效果?
答案是:两个都是。
我们如果换一种素材,大家就能知道非连续图形变化也是动画了。
效果分别为连续旋转和断续旋转。
后面一张图就是非连续变化下的效果,反而是我们实际项目中正确的菊花loading效果。
而实现的关键就是本文要深入介绍的steps()
功能符。
二、一句话介绍steps()功能符
steps()
功能符可以让动画不连续。
三、steps()在CSS3 animation中地位
steps()
功能符和CSS3 animation中的cubic-bezier()
功能符的地位和作用是一样的,都可以作为animation-timing-function
的属性值。
只不过steps()
更像是楼梯坡道,cubic-bezier()
更像是无障碍坡道。如下图示意:
然后steps()
简化出了step-start
和step-end
这两个关键字;cubic-bezier()
则有linear
,ease
,ease-in
,ease-out
以及ease-in-out
。
cubic-bezier
指三次贝塞尔曲线,具体可参见我之前文章“深度掌握SVG路径path的贝塞尔曲线指令”。
steps()
指逐步运动,下面进一步深入介绍。
四、搞清楚steps()中的start
和end
steps()
有一定的学习难度,总是搞不清楚,最主要就是start
和end
傻傻分不清楚。我这里自我挑战下,看看能不能说清楚。
常见steps()
用法举例:
steps(5, end); steps(2, start);
用steps()
语法表示就是:
steps(number, position)
其中:
- number
- 数值。这个很好理解,表示把我们的动画分成了多少段。
假设有如下CSS3动画
keyframes
,定义了一段从0~100px
的位移:@keyframes move { 0% { left: 0; } 100% { left: 100px; } }
假设我们的
number
值是5,则相当于把这段移动距离分成了5段,如下示意图: - position
- 关键字。表示动画是从时间段的开头连续还是末尾连续。支持
start
和end
两个关键字,含义分别如下: start
:表示直接开始。end
:表示戛然而止。是默认值。
为什么position非常难理解?
我认为两个原因:
steps()
属于timing function,也就是时间函数,时间这个东西是虚的,看不见,摸不着,联想乏力,所以认知成本高。这也是为什么那么多人都不珍惜时间的原因——无法感知。- CSS规范中对于
start
和end
的定义是基于数学函数来的,函数这东西,多少人的噩梦,因为过于抽象,与现实难以关联,所以,如果我们盯着定义去理解start
和end
,那就是死胡同,不归路,就算现在弄懂了,过段时间再遇到,得了,全忘光光了,函数图哪个是哪个,鬼才记得。下面这张图就出自规范文档:按照规范图再细化解释就是:
start
:表示直接开始。也就是时间才开始,就已经执行了一个距离段。于是,动画执行的5个分段点是下面这5个,起始点被忽略,因为时间一开始直接就到了第二个点:end
:表示戛然而止。也就是时间一结束,当前距离位移就停止。于是,动画执行的5个分段点是下面这5个,结束点被忽略,因为等要执行结束点的时候已经没时间了:
基于现实感知重新理解position!
万物具有相对性。例如,苍蝇眼中的人类动作都是慢动作,但是人类眼中的苍蝇却非常敏捷。
同样的,start
和end
这里的开始和结束是相对于时间而言的,但是,如果站在人类可感知的具体事物而言,start
和end
却是相反的含义。
所以,我们可以这么理解:
start
:表示结束。分段结束的时候,时间才开始走。于是,动画执行的5个分段点是后5个点:end
:表示开始。分段开始的时候,时间跟着一起走。于是,动画执行的5个分段点是前5个点:
记住position参数的含义
牢记这么一句话:一切都是反的!start不是开始,而是结束;end不是结束,而是开始。
五、step-start和step-end
step-start
和step-end
是steps()
功能符简化关键字,注意,是step-*
,step
,后面没有s
。
其中,step-start
等同于steps(1, start)
,step-end
等同于steps(1, end)
或者steps(1)
。
step-start
和step-end
用中文短句解读就是:一步到位和延迟到位,在实际项目中有什么作用呢?
对于只有0%,100%
或from, to
两个关键时间帧的动画,step-start
和step-end
是没有任何需要使用的理由的。
如果是非等分,无法过渡的阶梯动画,则有使用价值,例如下面这个基于box-shadow
实现的打点动画效果:
实现核心代码如下(此打点方法最多排纯CSS打点动画第3位,更多见此文介绍和指引):
<button>订单提交中<span class="dotting"></span></button>
.dotting { display: inline-block; min-width: 2px; min-height: 2px; margin-right: 8px; box-shadow: 2px 0, 6px 0, 10px 0; animation: dot 4s infinite step-start both; } @keyframes dot { 25% { box-shadow: none; } /* 0个点 */ 50% { box-shadow: 2px 0; } /* 1个点 */ 75% { box-shadow: 2px 0, 6px 0; } /* 2个点 */ }
六、steps()与填充模式animation-fill-mode
animation-fill-mode
有时候也会影响steps()
的断点表现,例如下面这个语句:
animation: move 5s forwards steps(5, end);
动画只执行一次,因为没有设置infinite
无限循环,而forwards
虽然表示“前”,但同样和现实表现是反的,也就是动画结束时候元素保持动画关键帧最后的状态。于是,下面6个分段点都会执行,整个动画停止在第6个分段点上。
这显然不是我们想要的,怎么处理呢?
可以消减分段个数和动画运动的跨度,调整如下:
@keyframes move { 0% { left: 0; } 100% { left: 80px; } }
也就是原来终点100px
改成80px
,同时CSS调用改成:
animation: move 5s forwards steps(4, end);
也就是原来steps(5, end)
改成steps(4, end)
,最后100%
这一帧交给forwards
即可!
七、结束语
steps()
是一个用得比较多的功能符,除了本文展示的loading效果,在逐帧动画中用得非常多。
例如,一些不太复杂的闪屏效果,写小而美的动画效果等(有名的如:点赞礼花效果)。
深入理解steps()
的好处在于,当我们在实现这些动画效果时候,省去了很多查阅文档套用语法的时间,还省掉了很多一个一个number
值调试的时间。可以让你干活效率加倍,有更多时间学习其他东西,形成正向循环,久而久之,必当学有所成。
就这些,感谢您花宝贵时间一直阅读到这里!
欢迎交流!
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:http://www.zhangxinxu.com/wordpress/?p=7618
(本篇完)
- 小tip: CSS3 animation渐进实现点点点等待提示效果 (0.338)
- 再说CSS3 animation实现点点点loading动画 (0.338)
- 小tips: 纯CSS实现打字动画效果 (0.232)
- 时隔两年,Chrome也支持round等CSS数学函数了 (0.232)
- 这回试试使用CSS实现抛物线运动效果 (0.152)
- 贝塞尔曲线与CSS3动画、SVG和canvas的基情 (0.132)
- SVG任意图形path曲线路径的面积计算 (0.127)
- 我使用CSS模拟个假的数字loading效果 (0.127)
- CSS3 Transitions, Transforms和Animation使用简介与应用展示 (0.025)
- CSS3 box-shadow兼容loading效果兼IE10+ CSS Hack介绍 (0.025)
- 理解CSS3 max/min-content及fit-content等width值 (RANDOM - 0.005)
animation中的cubic-bezier() 是不是改成 transition中的cubic-bezier() 更好?
这里有点模糊,是不是steps分段num是作用在动画的每段变化之间,而不是指整个动画
例如下例,是作用在0到50%,50%到100%之间,而不是0到100%之间
@keyframes move {
0% { left: 0; }
50% { left: 50px; }
100% { left: 100px; }
}
把动画的时间调忙就可以清晰的看到 start 和 end 时,第一帧 是不是灰色的,感谢提供了个很直观的demo, 666
这个steps()确实理解有难度,除了张大神写的这篇文章,其它我看过的博文都是云里雾里,高深莫测
恭喜张大神挑战成功,疯狂打CALL
实验了一下,step-start和step-end,果然是在相同的时间段的动画中一个在时间开始时到达第一个节点,一个是在时间结束是到达的时间节点,有点意思
刷新了半天,我以为图没有加载完
啊哈哈哈哈哈哈哈哈哈,我和你一样啊哈哈
个人理解: timing 函数 y=f(x) 是一个从时间到动画进程的映射函数,也就是那个坐标系,横坐标 x 表示时间,纵坐标 y 表示动画进程的百分比。所以感觉挺好理解的呀,steps(number, position) 定义一个 timing 函数,时间 x 被分成 number 段,position 为 start 表示 y 值在每段的开始就步进,end 表示 y 值在每段时间的结尾才产生步进。y值不变化就表示动画进程没变化。所以,一条线平着走就表示时间在走但是动画一直不前进。时间永远都在走是挡不住的,timing 函数能控制的是时间走多少,动画进程要前进多少。
发现问题了。start end 表示的是动画发生步进瞬间是在时间段的开始还是结束。
假设动画时间是 5 秒
step(5, start):
O O O O O
| — | — | — | — | — |
0s 5s
发生步进瞬间分别是 0s 1s 2s 3s 4s, 每次步进值 1/5
step(5, end):
O O O O O
| — | — | — | — | — |
0s 5s
发生步进瞬间分别是 1s 2s 3s 4s 5s, 每次步进值 1/5
我咋感觉有点晕
我也是想说,兼容好像有问题吧
这个兼容性好像不太好
您好,张老师,想咨询一个问题 js 插件 开头 有几种 写法 实在蒙了
之前做的一个点赞特效就用到了这个,但是感觉start跟end结果都一样
https://codepen.io/jackpan/pen/JZyXdM
你是21张图片,如果用step(20,end)的话,你会发现动画是从1-20,用start 就是 2-21张找图片,你自己放慢看一下就看出来,谢谢你这个例子让我更直观了一点,哈哈
是不是可以把start或者end去掉,直接steps(20)
把动画的时间调忙就可以清晰的看到 start 和 end 时,第一帧 是不是灰色的,感谢提供了个很直观的demo, 666
有点不好消化,多啃几次