CAMediaTiming--beginTimetimeOffset

Posted six6

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CAMediaTiming--beginTimetimeOffset相关的知识,希望对你有一定的参考价值。

背景介绍:beginTime、timeOffset属性来自CAMediaTiming,一个处理时间的协议,被CALayer和CAAnimation实现。

它们的官方注释:

技术分享图片

 

通过注释我们知道:

1.beginTime是相对于父对象的时间(也就是说是个相对值?)

2. t = (tp - begin) * speed + offset, 解释一下:

t:本对象时间(可能是CALayer和CAAnimation)

tp:父对象时间(可能是superLayer和CAAnimationGroup)

begin:即beginTime,开始时间

speed:速度

设置点通用前提,转换这个公式就可以得到:

3.timeOffset = t - (tp - begin)       (当speed=1)

4.begin = tp - t             (当speed=1, timeOffset = 0)

5.timeOffset = t - tp        (当speed=1, begin = 0)

注意4 和 5,  它们等号右边为相反数,这就很有意思了,记住它,后面的boss能不能攻克就靠这点有关

 

然而,光看注释无法解答我的问题:

1.它们怎么用?

2.CALayer和CAAnimation怎么混合使用这些属性?

3.本地时间和父对象时间的关系是什么?

4.怎样让动画组合在时间上衔接恰当?

 

注释无法解决,就百度、谷歌,如果还是找不到答案呢?

所以我通过测试的方法来深刻学习,也因此本文大多时间只是呈现出各种测试现象,需要读者自己通过这些现象自己思考。demo地址:(稍后上传)

 

在进入正文之前,还需讲下两个知识点:

1.CACurrentMediaTime()

/* Returns the current CoreAnimation absolute time. This is the result of
 * calling mach_absolute_time () and converting the units to seconds. */

CA_EXTERN CFTimeInterval CACurrentMediaTime (void)

调用它可以获取一个时间,图层的时间线都是以它为基准。CACurrentMediaTime()将mach_absolute_time()返回的结果转化为秒得到。mach_absolute_time()返回cpu里内建时钟运转的周期数(ticks),这个tick数,在每次手机重启之后,会重新开始计数,而且iPhone锁屏进入休眠之后tick也会暂停计数。

2.

- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(nullable CALayer *)l;
- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(nullable CALayer *)l;

本图层时间和父图层时间的转换。

 

先举个列子,让我们对beginTime、timeOffset有个简单的了解,这里我要假设你对UIBezierPath动画已经很好掌握(毕竟UIBezierPath动画不是本文内容内)。

动画是这个样子滴:(最后自己下载demo来看效果,不然看我的文字描述,你不一定清楚)

技术分享图片

点击add按钮就执行下面代码,飞机立即沿着绿色的线3秒内匀速飞行,然后回到原点。

代码1:

    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    animation.path = self.shapeLayer.path;
    animation.duration = 3;
    animation.delegate = self;
    animation.rotationMode = kCAAnimationRotateAuto;
    [self.airplaneLayer addAnimation:animation forKey:@"airplaneLayer"];

a.然后我添加animation.beginTime = 2,运行后飞机没动。

b.然后修改为animation.beginTime = CACurrentMediaTime() + 2,运行后飞机等待2秒后开始动画。

c.继续修改animation.beginTime = CACurrentMediaTime() - 2,运行后飞机立即开始动画,但只执行了2~3秒(即动画循环的最后一秒)的动画。

通过这三个测试,我们发现有点意思了。

我们推测出当animation.beginTime等于0时,会被系统替换成CACurrentMediaTime(),代表当前时间,立即开始动画,所以加一减一分别代表过去和将来。

脑洞再大一点,我们不该把自己限制在0~3秒的时间范围里,而应该是(-∞,+∞)时间线上。

图1     x:时间

技术分享图片           

图2      x:时间

技术分享图片

所谓的beginTime就是在图2的x轴上移动。对,就是这样,一目了然。

 

下面我们把beginTime的设置删掉,我们测算下timeOffset。

a.在代码1中添加animation.timeOffset= 2,效果为飞机在轨道的三分之二的位置开始飞行,飞行到终点后,又瞬间回到起点继续运行到轨道的三分之二的位置,动画终止。

b.修改成animation.timeOffset = -1,效果同上。

对比下beginTime,发现它们实现的效果是有所类似的,但又有区别。

类似的地方是:它们都可以改变飞机飞行起点。

不同处:beginTime设置为将来时间时会等待执行,但timeOffset都是立即执行;

    beginTime设置为过去时间时动画时间不足duration时间,但timeOffset会执行完duration时间。

不同处,我们可以与前面推导的公式4和公式5联系起来想想。

 

接下来,测试t = (tp - begin) * speed + offset及分析本地时间和父对象时间的关系

代码2

- (void)test {
    CALayer *layer1 = [CALayer new];
    CALayer *layer2 = [CALayer new];
    CALayer *layer3 = [CALayer new];
    [layer1 addSublayer:layer2];
    [layer2 addSublayer:layer3];
    [self.view.layer addSublayer:layer1];
    
    layer2.beginTime = 1;
    layer3.beginTime = 2;
    
    CFTimeInterval absoluteTime = CACurrentMediaTime();
    CFTimeInterval localTime1 = [layer1 convertTime:absoluteTime fromLayer:nil];
    CFTimeInterval localTime2 = [layer2 convertTime:absoluteTime fromLayer:nil];
    CFTimeInterval localTime3 = [layer3 convertTime:absoluteTime fromLayer:nil];
    
    NSLog(@"\\nabsoluteTime:%2.lf, \\nlocalTime1:%2.lf, \\nlocalTime2:%2.lf, \\nlocalTime3:%2.lf", absoluteTime, localTime1, localTime2, localTime3);
    
    CFTimeInterval localTime3f1 = [layer3 convertTime:absoluteTime fromLayer:layer1];
    CFTimeInterval localTime3f2 = [layer3 convertTime:absoluteTime fromLayer:layer2];
    
    CFTimeInterval localTime3t1 = [layer3 convertTime:absoluteTime toLayer:layer1];
    CFTimeInterval localTime3t2 = [layer3 convertTime:absoluteTime toLayer:layer2];
    
    CFTimeInterval localTime1f3 = [layer1 convertTime:absoluteTime fromLayer:layer3];
    CFTimeInterval localTime1t3 = [layer1 convertTime:absoluteTime toLayer:layer3];
    
    NSLog(@"图层间时间转换\\nlocalTime3f1:%2.lf, \\nlocalTime3f2:%2.lf, \\nlocalTime3t1:%2.lf, \\nlocalTime3t2:%2.lf, \\nlocalTime1f3:%2.lf, \\nlocalTime1t3:%2.lf", localTime3f1, localTime3f2, localTime3t1, localTime3t2, localTime1f3, localTime1t3);
}

打印:

absoluteTime:374474, 
localTime1:374474, 
localTime2:374473, 
localTime3:374471
 图层间时间转换
localTime3f1:374471, 
localTime3f2:374472, 
localTime3t1:374477, 
localTime3t2:374476, 
localTime1f3:374477, 
localTime1t3:374471

这里我就不过多分析了,读者自己考虑下,提一下就是localTime3f1和localTime1t3结果相同。

代码3

 

/* Additional offset in active local time. i.e. to convert from parent
 * time tp to active local time t: t = (tp - begin) * speed + offset.
 * 验证这句话
 */
- (void)test2 {
    CALayer *layer1 = [CALayer new];
    CALayer *layer2 = [CALayer new];
    CALayer *layer3 = [CALayer new];
    [layer1 addSublayer:layer2];
    [self.view.layer addSublayer:layer1];
    
    layer2.beginTime = 3;
    layer2.speed = 2;
    layer2.timeOffset = 5;
    
    CFTimeInterval absoluteTime = CACurrentMediaTime();
    CFTimeInterval t = [layer1 convertTime:absoluteTime fromLayer:nil];
    CFTimeInterval tp = [layer2 convertTime:absoluteTime toLayer:nil];
    
    CFTimeInterval t0 = (tp - layer2.beginTime) * layer2.speed + layer2.timeOffset;
    
    NSLog(@"t:                              %.2lf", t);
    NSLog(@"(tp - begin) * speed + offset:  %.2lf", t0);
}

 

打印:

 t:                             375488.09
(tp - begin) * speed + offset:  375488.07            

或许你认为对beginTime、timeOffset的讲解快完了,它们也就这样,但事实是这只是开始。

通过飞机动画,我们了解到它们在CAAnimation上的作用,但它们在CALayer上的效果又是怎样的呢?对CAAnimation又有什么影响呢?

 

 

 

好了,现在我们对beginTime、timeOffset有了一定了解,下面让我们暂定、重启一个动画来实践下。

 

以上是关于CAMediaTiming--beginTimetimeOffset的主要内容,如果未能解决你的问题,请参考以下文章