iOS 开发之动画中的时间

引言

在iOS开发中使用动画时,可以通过设置动画的duration、speed、begintime、offset属性,来设置动画的时长、速度、起始时间及起始偏移。 用一个简单的例子来说明各个参数的的作用。动画很简单,一个红色的方块从左移到右边。动画的持续时间是1s,没有重复,效果如下。

    CFTimeInterval currentTime = CACurrentMediaTime();
    CFTimeInterval currentTimeInLayer = [self.testLayer convertTime:currentTime fromLayer:nil];
    CFTimeInterval addTime = currentTimeInLayer;
    anim.beginTime = 0.3 + addTime;
    [anim setTimeOffset:0.5];
    [anim setSpeed:2];

做修改以后,效果如下:

与上面相比,三处不同

  1. 动画的速度是原来的两倍。
  2. 点击开始动画的按钮,到开始动画,有一个延迟。
  3. 动画起始时,滑块的位置为中央,而不是在左边。

我们已经看到了这些属性的效果。翻阅文档,发现begintime、speed等属性是CAMediaTiming这协议的属性,并且CALayer、CAAnimation都遵守了CAMediaTiming协议。

那么CAMediaTiming协议是什么呢?有什么作用呢?

层级时间结构

根据文档,CMediaTiming协议构建了一个层级的时间系统,并用这个层级的时间系统来协调各个layer、animation的时间。

这个协议被CAAnimation及CALayer遵守,每一个遵守协议的的object对应一个time space。根据object之间的关系,不同的time space有层级关系。比如Layer A有一个subLayer B,那么Layer A对应的time space就是layer B对应的time space的parent time space。每一个time space中时间的数值都是根据parent time space的数值,以及begintime、speed等属性,根据一定的规则来计算的。

为了便于理解层级时间系统,先看下layer在屏幕上的显示位置是如何确定的,然后做一个类比。

layer层级如上。要确定sublayer1在屏幕上的显示位置,一共分三步。

  1. 确定window layer在屏幕位置position1
  2. 根据position1及view layer的position属性,确定view layer在屏幕中的位置position2
  3. 根据position2及sublayer1的position属性,确定sublayer1在屏幕中的位置position3

与此类似,要确定sub1ayer1中的time,也要分三步。

  1. 确定window layer中的time1
  2. 根据time1及view layer的begintime、offset等属性计算出view layer中的time2
  3. 根据time2及sublayer1的begintime、offset等属性计算出sublayer中的time3

和确定layer的位置相比,确定时间有一些复杂,主要提现在下面两点

1 . 层级时间系统的构成复杂。 layer tree的每一级都是CALayer,而只要遵守CAMediaTiming协议,就可以作为层级时间系统的一部分。比如CALayer、CAAnimation(及其子类CAAnimationGroup)都可以作为层级时间系统的一部分。

2 .不同层级之间时间转换规则复杂 计算当前layer的位置时,只需要知道父layer的位置,以及当前layer的position属性。计算当前层级时间时,不仅需要知道上一个层级的时间,还需要知道当前层级的begintime、offset、speed等属性。转换的规则也比较复杂,要经历两次转换。从parent time到active local time,再到basic local time。

active local time

这次转换是为了处理当前层级的object在父层级的的时间线上的位置,以及当前层级和父层级之间时间流逝速度的关系。

和这次转换相关的属性有beginTime、speed以及timeOffset

  1. begin time 子层级相对于父层级的起始时间。也就是父层级的时间经过多久,子层级才开始计算时间。 比如子层级A被加入层级时间系统时,它父层级B的时间是5s,子层级A的begintime是6s,那么当它父层级的时间变为6s时,子层级才开始计算时间。
  2. speed 子层级相对于父层级的时间流逝速度。如果speed是2,那么当父层级的时间增加了10s时,子层级的时间增加了20s(10s的2倍)。
  3. timeOffset 为本地时间增加一个偏移。 如果timeOffset是5s,那么本地时间的起始就是5s。 从parent time到active local time有一个公式,可以用来参考。
t = (tp - begin) * speed + offset
basic local time

这次转换是为了处理当前层级的重放(repeat)、以及重放之前是否要倒放(play backward)等操作。

比如当前层级是一个动画(CAAnimation遵守CAMediaTiming协议),duration是1s,经过第一次转换之后的active local time是5.5s。如果动画的repeatCount是10,那么经过第二次转化以后,basic local time会是0.5s,因此当前是动画展示一半的状态。

1 .repeatCount及repeatDuration 当前的层级要重复的次数或重复的时间,两者不可同时指定。 以动画为例,如果指定repeatCount,那么指定了动画要重复几次。如果指定了repeatDuration,那么指定了动画重复的时间。 2 .autoreverses 在重复之前是否要倒放。

文首的例子

根据这些知识,可以解释文章开始时设置参数的效果。 当动画被加到layer上时,动画对应的time space被加到层级时间系统中,是layer对应的time space的子层级。

1 .动画的速度是原来的两倍 设置动画的speed是2,这样子动画中的时间流逝速度时layer中时间流逝速度的2倍。当layer中时间经过0.5s时,动画中时间已经流逝了1s,动画已经完成了。(动画的duration是1s) 2 .点击开始动画的按钮,到开始动画,有一个延迟 我们首先得到了当前layer的时间addtime,然后把动画的begintime设置为addtime+0.3。这样子当动画被加到layer之后0.3s,layer中的时间是addtime+0.3,此时动画中的时间才开始计算,之前动画没有开始。 3 .动画起始时,滑块的位置为中央,而不是在左边 我们设置了动画的offset为0.5s。当动画开始时,动画对应的time space的时间是0.5s,对应动画duration的一半,即滑块位置在屏幕中央。

更多应用

了解了CAMediaTiming协议后,可以实现很多动画的效果。

  1. 让某一个layer上的动画停止 设置layer的speed为0即可。
  2. 实现门打开然后关闭的效果 实现一个门打开的动画,然后把动画的autoreverses属性设置为YES即可。
  3. layer上的若干动画依次延迟启动 分别设置这些动画的beginTime为不同的值即可
  4. 手动控制动画的进度 设置动画的speed为0,然后改变动画的offset即可。

苹果已经把工具给我们了,可以做出什么样的产品就看大家的想象力了。

参考

控制动画时间: http://ronnqvi.st/controlling-animation-timing/

Time Warp in Animation:http://wangling.me/2011/06/time-warp-in-animation.html#fn-1

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏漫漫全栈路

Vim勉强入个门

最近在Linux环境下使用的情况变多了,再加上.NET Core的学习,需要在Linux下编辑文件,因此打算好好学习下传说中的陆上最强编辑器?(我只是听说,关...

3034
来自专栏mukekeheart的iOS之旅

IOS学习——iphone X的适配

  说实话,对于一个刚入门iOS两个月的新手而言,在拿到这个任务的时候整个人都是懵逼的,怎么做适配?哪些地方需要适配?该怎么做?一个个问题搞得头都大了。   首...

2896
来自专栏极客编程

html5 canvas 与小丑。

  以前开发动画应用你可能需要学习很复杂的动画制作框架。自从HTML5画布(Canvas)功能面世后,Web动画就一下子从云端跌落到了地面——任何一个Web程序...

572
来自专栏林德熙的博客

win10 uwp 截图 获取屏幕显示界面保存图片 水印

本文主要讲如何保存我们的屏幕显示的控件,保存为图片。这个也就是截图,截我们应用显示的,我们应用之外的不截图。

501
来自专栏前端知识分享

第18天:京东网页头部制作

一、京东页面制作开始(头部) 1、浮动的盒子宽度由内容定,不需要设置宽度 2、绝对定位不占位置,相对定位占位置

802
来自专栏漫漫全栈路

Vim勉强入个门--循序渐进陆上最强编辑器

Vim作为一个编辑器,那么基本的功能当然就是编辑喽,所以先把它的编辑功能用好。

2292
来自专栏一“技”之长

AppleWatch开发入门九——Watch帧动画的实现

        动画一直是iOS系统的一大亮点,CoreAnimation和粒子效果的支持,开发者可以很容易的做出效果炫酷的动画特效。在watchOS中,由于性...

722
来自专栏Porschev[钟慰]的专栏

jQuery Gallery Plugin在Asp.Net中使用

jQuery Gallery Plugin在Asp.Net中使用 推荐一个简单易用的Gallery插件:jQuery Gallery Plugin 下面是在As...

1719
来自专栏闻道于事

第二天0605下午——超链接<a>与图片<img>

今天下午学习了超链接<a>标签和图片<img>标签,以下面代码为例: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...

2828
来自专栏WindCoder

iOS 下 input=text 等输入框,触发时,灰色背景

这个属性只用于iOS (iPhone和iPad)。当你点击一个链接或者通过Javascript定义的可点击元素的时候,它就会出现一个半透明的灰色背景。要重设这个...

911

扫码关注云+社区