Core Animation Guide 里有一段关于暂停和恢复动画的示例代码,如下:
-(void)pauseLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
-(void)resumeLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
主要原理就是利用设置当前layer的speed为0,动画就会暂停;当恢复为1时,动画会继续。然而,想要做到暂停和恢复,还要设置好暂停、�以及恢复时的状态。
刚看到这段代码, 我其实是很晕的。首先,为什么convertTime:from: 的第二个参数居然可以为nil。第二,在恢复的时候beginTime先设置为0,再设置成另一个值。还有beginTime�为什么是这样算的。浏览了一下官方文档,其中对于 speed , begin 和 timeOffset 的描述基本来自与CAMediaTiming这个协议,而且相当简洁,看了就有一种说了等于没说的�感觉-_-!
没办法,只好靠自己了。
首先看看这些数字是怎么变化的。��
--------------------Pause--------------------
CACurrentMediaTime: 65716.046179
pausedTime(timeOffset): 65716.046179
--------------------Resume--------------------
CACurrentMediaTime: 65717.960595
begineTime: 1.914415
--------------------Pause--------------------
CACurrentMediaTime: 65721.944880
pausedTime(timeOffset): 65720.030464
--------------------Resume--------------------
CACurrentMediaTime: 65725.561110
begineTime: 5.530646
--------------------Pause--------------------
CACurrentMediaTime: 65727.395533
pausedTime(timeOffset): 65721.864888
--------------------Resume--------------------
CACurrentMediaTime: 65730.828135
begineTime: 8.963247
--------------------Pause--------------------
CACurrentMediaTime: 65732.445991
pausedTime(timeOffset): 65723.482744
...
这些数字还是挺有规律的,�比如暂停时,
pausedTime = CACurrentMediaTime - layer.beginTime
再有beginTime只增不减,每次增加的时间等于暂停的时间。CACurrentMediaTime获取的�是当前的绝对时间。然而只有这些还是没有办法看出这些值为什么应该是这样。
就在一筹莫展的时候,在��代码里点�进去每个属性看一圈,终于发现比�较有意思的东西了:
/* The begin time of the object, in relation to its parent object, if
* applicable. Defaults to 0. */
public var beginTime: CFTimeInterval { get set }
...
/* Additional offset in active local time. i.e. to convert from parent
* time tp to active local time t: t = (tp - begin) * speed + offset.
* One use of this is to "pause" a layer by setting `speed' to zero and
* `offset' to a suitable value. Defaults to 0. */
public var timeOffset: CFTimeInterval { get set }
beginTime是相对于父layer的,文档也有说明。怎么个相对法?下面的timeOffset给出了答案:
t = (tp - begin) * speed + offset
其中t是当前layer的�“本地”时间,tp是父layer的时间。这么一来就可以解释暂停时的pausedTime的具体含义了。
暂停时只调用了convertTime:from: 并��将其设置为layer的timeOffset, 其值为
CACurrentMediaTime - layer.beginTime。
注意,在恢复�动画时offset是重置为0的;其中CACurrentMediaTime 与父layer的时间是同步的,因为父layer的speed一直都是默认的1。因此用上面的�公式 , 子layer的时间应该是 :
(CACurrentMediaTime - layer.beginTime)*1 + 0
这个值和pausedTime的值是�一样的,从上面总结出的数字规律中可以知道。因此这个时间就是在动画�暂停时,子layer的时间。
现在我们知道[layer convertTime:CACurrentMediaTime() fromLayer:nil] 的�意义了,它就是把�父layer的时间转换为子layer的时间。这个nil应该和layer.superLayer是一样的。把它改成layer.superLayer,�对结果没有任何影响。可以猜测,convertTime:fromLayer: 这个方法当第二个参数为nil时,它�的实现就是(time - begin) * speed + offset.
把layer.timeOffset设置成这个值就是为了防止动画暂停时layer回到原点。
下面我们来看看恢复动画时的逻辑。
现在我们应该�明白�怎么去实现了。我们需要让子layer的时间仍然从pausedTime开始。根据公式:
t = (tp - beginTime) * speed + offset
假设当动画�恢复时,暂停�的总时长为s。 ���那么在调用CACurrentMediaTime()时,上式中的tp就增加了s,如果我们把offset重置为0,为了让t保持不变,只需要让beginTime也增加s就可以了。这就解释了为什么beginTime只�增不�减,且每次增加的时长刚好是暂停的时间(上面总结出的数字规律)。
�看代码:
-(void)resumeLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
可以看到,offset重置为0了,beginTime就是�设置成了暂停的时间,符合我们之前的推断。至于beginTime为什么先设为0,大家应该都清楚了吧?