实现动画方式深度解析(十) —— Core Animation之高级动画技巧 (七)

版本记录

版本号 时间
V1.0 2017.09.23

前言

app中好的炫的动画可以让用户耳目一新,为产品增色不少,关于动画的实现我们可以用基本动画、关键帧动画、序列帧动画以及基于CoreGraphic的动画等等,接下来这几篇我就介绍下我可以想到的几种动画绘制方法。具体Demo示例已开源到Github —— 刀客传奇,感兴趣的可以看我写的另外几篇。
1. 实现动画方式深度解析(一) —— 播放GIF动画(一)
2. 实现动画方式深度解析(二) —— 播放GIF动画之框架FLAnimatedImage的使用(二)
3. 实现动画方式深度解析(三) —— 播放序列帧动画(一)
4. 实现动画方式深度解析(四) —— QuartzCore框架(一)
5. 实现动画方式深度解析(五) —— QuartzCore框架之CoreAnimation(二)
6. 实现动画方式深度解析(六) —— Core Animation Basics(三)
7. 实现动画方式深度解析(七) —— Core Animation之Setting Up Layer Objects(四)
8. 实现动画方式深度解析(八) —— Core Animation之动画层内容 (五)
9. 实现动画方式深度解析(九) —— Core Animation之构建图层层级 (六)

Advanced Animation Tricks - 高级动画技巧

有很多方法可以配置基于属性或关键帧的动画,为您做更多的事情。 需要一起或顺序执行多个动画的应用程序可以使用更高级的行为来同步这些动画的时间或将它们链接在一起。 您还可以使用其他类型的动画对象来创建视觉转换和其他有趣的动画效果。


Transition Animations Support Changes to Layer Visibility - 转换动画支持更改层可见性

顾名思义,转换动画对象为图层创建动画视觉转换。 过渡对象最常见的用途是以协调的方式动画化一层的外观和另一层的消失。 与基于属性的动画不同,动画更改图层的一个属性,转换动画会操纵图层的缓存图像,以创建通过单独更改属性难度或不可能执行的视觉效果。 转换的标准类型可让您执行显示,推送,移动或交叉淡入淡出动画。 在OS X上,您还可以使用Core Image过滤器创建使用其他类型的效果(如擦除,卷曲,波纹或自定义效果)的转换。

要执行转换动画,您将创建一个CATransition对象并将其添加到转换中涉及的层。 您可以使用转换对象来指定要执行的转换类型以及转换动画的起点和终点。 您也不需要使用整个转换动画。 转换对象允许您指定要在动画时使用的开始和结束进度值。 这些值可让您在其中点进行动画开始或结束。

下面代码显示了用于在两个视图之间创建动画推送转换的代码。 在该示例中,myView1myView2都位于同一父视图中的相同位置,但只有myView1当前可见。 推送过渡会导致myView1向左滑动,并且渐隐,直到它隐藏,而myView2从右侧滑入并变为可见。 更新两个视图的隐藏属性可确保在动画结束时两个视图的可视性是正确的。

//Animating a transition between two views in iOS

CATransition* transition = [CATransition animation];
transition.startProgress = 0;
transition.endProgress = 1.0;
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
transition.duration = 1.0;
 
// Add the transition animation to both layers
[myView1.layer addAnimation:transition forKey:@"transition"];
[myView2.layer addAnimation:transition forKey:@"transition"];
 
// Finally, change the visibility of the layers.
myView1.hidden = YES;
myView2.hidden = NO;

当同一个转换中涉及到两个层时,可以使用相同的转换对象。 使用相同的转换对象也简化了必须编写的代码。 但是,您可以使用不同的转换对象,如果每个层的转换参数不同,那么肯定需要这样做。

下面代码显示了如何使用Core Image过滤器在OS X上实现过渡效果。在使用所需参数配置过滤器后,将其分配给过渡对象的过滤器属性。 之后,应用动画的过程与其他类型的动画对象相同。

// Using a Core Image filter to animate a transition on OS X

// Create the Core Image filter, setting several key parameters.
CIFilter* aFilter = [CIFilter filterWithName:@"CIBarsSwipeTransition"];
[aFilter setValue:[NSNumber numberWithFloat:3.14] forKey:@"inputAngle"];
[aFilter setValue:[NSNumber numberWithFloat:30.0] forKey:@"inputWidth"];
[aFilter setValue:[NSNumber numberWithFloat:10.0] forKey:@"inputBarOffset"];
 
// Create the transition object
CATransition* transition = [CATransition animation];
transition.startProgress = 0;
transition.endProgress = 1.0;
transition.filter = aFilter;
transition.duration = 1.0;
 
[self.imageView2 setHidden:NO];
[self.imageView.layer addAnimation:transition forKey:@"transition"];
[self.imageView2.layer addAnimation:transition forKey:@"transition"];
[self.imageView setHidden:YES];

注意:在动画中使用Core Image过滤器时,最棘手的部分是配置过滤器。 例如,使用条形滑动转换,指定输入角度太高或太低可能会使它看起来好像没有转换发生。 如果您没有看到您想要的动画,请尝试将过滤器参数调整为不同的值,以查看是否更改了结果。


Customizing the Timing of an Animation - 自定义动画的时序

Timing是动画的重要组成部分,Core Animation可以通过CAMediaTiming协议的方法和属性为动画指定精确的时间信息。 两个核心动画类采用这个协议。 CAAnimation类采用它,以便您可以在动画对象中指定时间信息。 CALayer还采用它,以便您可以为隐式动画配置一些与时序相关的功能,尽管包含这些动画的隐式事务对象通常提供优先级的默认时序信息。

当考虑时间和动画时,了解层对象如何随着时间的推移是重要的。 每个层都有自己的本地时间,用于管理动画时序。 通常,两个不同层的本地时间足够接近,您可以为每个层指定相同的时间值,并且用户可能不会注意到任何内容。 然而,层的本地时间可以由其父层或其自己的时序参数修改。 例如,更改图层的速度属性会导致该图层(及其子图层)的动画持续时间按比例更改。

为了帮助您确定时间值适用于给定层,CALayer类定义convertTime:fromLayer:convertTime:toLayer:方法。 您可以使用这些方法将固定时间值转换为图层的本地时间,或将时间值从一个层转换为另一层。 这些方法会考虑可能影响图层本地时间的媒体时序属性,并返回可与其他图层一起使用的值。 下面代码显示了一个示例,您应该定期使用以获取图层的当前本地时间。 CACurrentMediaTime函数是一个方便的函数,它返回计算机的当前时钟时间,该方法将采用该函数并将其转换为图层的本地时间。

 // Getting a layer’s current local time

CFTimeInterval localLayerTime = [myLayer convertTime:CACurrentMediaTime() fromLayer:nil];

一旦您在图层的本地时间中有时间值,您可以使用该值更新动画对象或图层的时间相关属性。 通过这些时序属性,您可以实现一些有趣的动画行为,其中包括:

  • 使用beginTime属性设置动画的开始时间。 通常,动画将在下一个更新周期开始。 您可以使用beginTime参数将动画开始时间延迟几秒钟。 将两个动画连接在一起的方法是设置一个动画的开始时间,以匹配另一个动画的结束时间。如果延迟动画的开始,您可能还需要将fillMode属性设置为kCAFillModeBackwards。 即使图层树中的图层对象包含不同的值,此填充模式也会使图层显示动画的起始值。 没有这种填充模式,您将看到在动画开始执行之前跳转到最终值。 其他填充模式也可用。

  • autoreverses属性会使动画在指定的持续时间内执行,然后返回到动画的起始值。 您可以将此属性与repeatCount属性组合,以在起始值和结束值之间来回动画。 将重复计数设置为自动转换动画的整数(例如1.0)会导致动画停止其起始值。 添加额外的一半步骤(例如重复计数为1.5)会导致动画停止其结束值。

  • 使用具有组动画的timeOffset属性可以在稍后时间启动一些动画。


Pausing and Resuming Animations - 停止和暂停动画

要暂停动画,您可以利用层采用CAMediaTiming协议并将层的动画速度设置为0.0。 将速度设置为零可暂停动画,直到将值更改回非零值。 下面代码显示了如何暂停和恢复动画的简单示例。

// Pausing and resuming a layer’s animations

-(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;

}

Explicit Transactions Let You Change Animation Parameters - 显式转场让您更改动画参数

对图层进行的每一次更改都必须是事务的一部分。 CATransaction类在适当的时间管理动画的创建和分组及其执行。 在大多数情况下,您不需要创建自己的事务。 每当向其中一个图层添加显式或隐式动画时,Core Animation将自动创建一个隐式事务。 但是,您也可以创建显式的事务来更精确地管理这些动画。

您可以使用CATransaction类的方法创建和管理事务。 开始(并隐式创建)一个新的事务调用begin类方法; 要结束该事务,调用commit类方法。 在这些调用之间是您想成为事务一部分的更改。 例如,要更改图层的两个属性,可以使用下面的代码。

// Creating an explicit transaction

[CATransaction begin];
theLayer.zPosition=200.0;
theLayer.opacity=0.0;
[CATransaction commit];

使用交易的主要原因之一是在显式事务的限制内,您可以更改持续时间,计时功能和其他参数。 您还可以为整个事务分配完成块,以便在动画组完成时通知您的应用。 更改动画参数需要使用setValue:forKey:方法修改事务字典中的相应键。 例如,要将默认持续时间更改为10秒,您可以更改kCATransactionAnimationDuration键,如下所示。

//Changing the default duration of animations

[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:10.0f]
                 forKey:kCATransactionAnimationDuration];
// Perform the animations
[CATransaction commit];

您可以在要为不同动画集提供不同默认值的情况下嵌套事务。 要将一个事务嵌套在另一个事务中,只需再次调用begin类方法。 每个开始调用必须通过对commit方法的相应调用进行匹配。 只有在您提交最外层事务的更改后,Core Animation才会启动相关联的动画。

下面代码显示了嵌套在另一个事务中的一个事务的示例。 在此示例中,内部事务将更改与外部事务相同的动画参数,但使用不同的值。

// Nesting explicit transactions

[CATransaction begin]; // Outer transaction

// Change the animation duration to two seconds

[CATransaction setValue:[NSNumber numberWithFloat:2.0f]

forKey:kCATransactionAnimationDuration];

// Move the layer to a new position

theLayer.position = CGPointMake(0.0,0.0);

[CATransaction begin]; // Inner transaction

// Change the animation duration to five seconds

[CATransaction setValue:[NSNumber numberWithFloat:5.0f]

forKey:kCATransactionAnimationDuration];

// Change the zPosition and opacity

theLayer.zPosition=200.0;

theLayer.opacity=0.0;

[CATransaction commit]; // Inner transaction

[CATransaction commit]; // Outer transaction

Adding Perspective to Your Animations - 为您的动画添加视觉

应用程序可以在三个空间维度中操纵图层,但为了简单起见,Core Animation使用平行投影显示图层,这将基本上将场景平坦化为二维平面。 这种默认行为导致具有不同zPosition值的相同大小的图层显示为相同的大小,即使它们在z轴上相距较远。 你通常会在三维中观看这样一个视角的观点已经消失了。 但是,您可以通过修改图层的转换矩阵来包含透视信息来更改该行为。

当修改场景的透视图时,需要修改包含正在查看的图层的超层的sublayerTransform矩阵。 修改超层通过将相同的透视信息应用于所有子层来简化您必须编写的代码。 它还确保透视图正确应用于在不同平面中彼此重叠的兄弟子层。

下面代码显示了为父层创建简单透视变换的方法。 在这种情况下,自定义eyePosition变量指定沿着Z轴的相对距离以查看图层。 通常你为eyePosition指定一个正值,以便按照预期的方式保持这些层面。 更大的值导致更平坦的场景,而较小的值会导致层之间更显着的视觉差异。

// Adding a perspective transform to a parent layer

CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = -1.0/eyePosition;
 
// Apply the transform to a parent layer.
myParentLayer.sublayerTransform = perspective;

配置父层后,您可以更改任何子层的zPosition属性,并根据其距离眼睛位置的相对距离观察其大小如何变化。

后记

未完,待续~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容