复习 animation-delay 负值以及 delay 正值在 iOS 上的坑

复习 animation-delay 负值以及 delay 正值在 iOS 上的坑

本文是对 animiation-delay 负值的理解,希望能搞清楚为什么在 iOS 上会发生 animation-delay 计算错误的问题。

animation delay 负值

animation 负 delay 是古早古早就有了的特性,但是平时用的比较少,也没怎么关注,有一天真的用起来(或者被问到)的时候就是一脸萌比。今天就研究一下负值的概念,以及用到它的场景。

Specifying a negative value for the animation delay causes the animation to begin executing immediately. However, it will appear to have begun executing partway through its cycle. For example, if you specify -1s as the animation delay time, the animation will begin immediately but will start 1 second into the animation sequence.

定义上看我们知道对一个动画设置 delay 为负数:

  1. 动画会立刻开始执行

  2. 开始的位置是动画的其中一个阶段

是一个很好理解的概念,让我们上一个 codepen 的例子说明一切:


动画时间都是 10s,只执行一次,开始和结束都是 #CCC。没有延迟的方块动画表现如上,在第 10 秒的时候动画就会停止;延迟 2s 开始的方块动画,在第 12 秒的时候动画停止;提前 2s (延迟 -2s)开始的方块动画,在第 8 秒的时候停止动画。

其实特别好理解,定义在 @keyframes 中的动画需要执行 animation-time 时间长度。animation-delay 为正数的时候,就是动画要延迟开始,animation-time 都还没有开始计算,正数的 delay 不会被计算到 animation-time 中,我们看到的动画就是从第一帧开始的;animation-delay 为负数的时候,意味着动画是提前开始的,animation-time 已经开始计算了,负数的 delay 是被算入 animation-time 中的,所以我们看到的动画是从某一帧开始的。

之前写企鹅 FM 的音频 loading 效果的时候 (所以专栏是不能传 gif 吗??),进入了一个 delay 的误区:场景是多个小元素组成的一个完整的需要无限循环 (infinite) 的整体,其中每个小元素都有与彼此不同得 delay 值。于是…就傻逼地陷入了“为什么新的一轮开始时,没有 delay 了?如何让 delay 在每次动画的循环里生效”的困惑中。


delay 只会在动画开始的时候生效,但作用效果是持续整个动画执行过程的。在我的例子中,多个小元素的 delay 值不同,就好像几个人一起跑步,大家的跑步能力相同(animation-time 相同),但是有人早开始有人迟开始(animation-delay 不同),无论是只跑一圈(animation-iteration-count: 1 )还是永无止境地在跑(animation-iteration-count: infinite ),跑得快的人总是在跑得慢的人得前边。如果每跑一圈,delay 就生效一次,那跑得慢的人就会越来越慢,在发令枪响之前就开跑的人(animation-delay 负值)……得飞出地球超过光速了。


delay 正值在 iOS 上的坑

delay 负值已经解释清楚了,所以为什么会突然需要用负值呢?源于这一 part 的标题… 还是在写上面企鹅 FM 的音频加载效果时遇到的。一言蔽之,在 iOS10 上 animation-delay 正值可能导致 iOS 无法正常计算 delay,导致动画效果和预期不符。 甚至现在 iOS7 和 iOS8 上这个 bug 原因(暂时)还找不到…

下图是表现正常的动画截图,可以看出小矩形条彼此高度不一:

下面的两张是在 iOS 测试的截图,左下图是页面中没有出现使用了 filter 属性,和上图相似,加载动画中每条小矩形高度不一;而右下图是页面中使用了 filter 属性,可以看出动画是有问题的,相邻小矩形高度相同:

具体代码可看这里(项目本身依赖了别的 less 文件,所以直接拷贝了生成的 css 文件 = =,我们是用 LESS 的嗷)。

是不是非常奇怪!是的!!!

但是我在各个地方都没有找到类似的 bug report,唯一一个稍微靠上边的是一个 13 年的 issue:页面滚动的时候会导致动画的时间线出现偏差。还有一个 SO 上一个性能极差导致的 delay 错误问题。再靠边一点的解释都木有找到,如果有人找到了,请一定要告诉我。

因为找不到理论支持,所以只能进行推测:由于 filter 在使用上最为人诟病的就是性能问题,而且去掉 filter 动画就正常了,只能推锅给 Safari 内核在渲染 filter 效果时会造成 timer 的混乱,导致动画开始的时间点不对,delay 是正数时,动画开始执行的时刻取决于相对于 iOS timer 算出的开始时间点;delay 是负数时,动画执行情况是看此时落在哪一个 keyframe 区域上:

用 delay 负值,只要 timer == 0 的时刻没有错,延迟就不会出错;用 delay 正值,鬼知道在 timer > 0 以后会发生什么影响 timer 的事情,导致延迟计时出问题。所以建议大家用 delay 负值来做动画~

// 当然,如果用 GSAP 什么的…可能就不会有这个问题了

// delay 正值在我这里的一个 demo 上(今天来不及整理代码了),在 iOS7 和 iOS8 也会有延迟问题。

编辑于 2016-11-30

文章被以下专栏收录