难度:⭐️
最终效果:
平时在逛一些设计相关网站时总想把一些效果实现一下,这次挑了 Google Material Design的练一下手。
我们可以把动画简单地分解为 路径填充 + 旋转:
仔细考虑一番,可以发现用改变 CAShapeLayer 的 strokeStart 和 strokeEnd 实现。整个填充看起来像 strokeEnd跑的快, strokeStart开始跑得慢,但在 strokeEnd跑到终点后突然加速追赶。
但似乎时间函数没那么好控制,因为它显得不那么规律。在这种情况下,我们可以使用关键帧动画实现这种不规则的时间函数控制。我简单地把它分为了三个时间点: 0,0.5,1.0。
时间点 0:strokeEnd出发了一点点,但 strokeEnd还没动。
时间点 0.5:strokeEnd已经跑到了终点,strokeEnd才跑了一点距离。
时间点 1.0:strokeEnd在终点等着,strokeStart加速赶到了终点。
经过此番分析后,动画相关的代码大致如下:
let strokeDuration:Double = 2
func strokeStartAnimation() -> CAKeyframeAnimation {
let animation = CAKeyframeAnimation(keyPath: "strokeStart")
animation.keyTimes = [0, 0.5, 1]
animation.values = [0, 0.3, 1]
animation.duration = strokeDuration
animation.fillMode = kCAFillModeForwards
animation.repeatCount = Float.infinity
return animation
}
func strokeEndAnimation() -> CAKeyframeAnimation {
let animation = CAKeyframeAnimation(keyPath: "strokeEnd")
animation.keyTimes = [0, 0.5, 1]
animation.values = [0.05, 1, 1]
animation.duration = strokeDuration
animation.fillMode = kCAFillModeForwards
animation.repeatCount = Float.infinity
return animation
}
把这两个动画加到了图层上,就能看到没有旋转情况下的效果。再完善一下,添加一个比路径填充稍微慢一点的旋转动画,就能达到文初那种似乎不规则的 loading效果。
不足之处:在路径动画一次结束后切换下一次时感觉没那么过渡自然,那是因为使用路径填充实现。如果使用改变路径长度实现会更好一点(CSS中就可以通过 stroke dash array/offset修改填充长度实现),但在 iOS中,CAShapeLayer的 lineDashPattern 不是 animatable的,导致不能按照 CSS的思路去实现。
其实可以自定义绘制,不过稍显麻烦,如果你追求 pixel perfect,那就那么做吧。
-EOF-