【原】iOSCoreAnimation动画系列教程(二):CAKeyFrameAnimation【包会】

=======================================================

转载请注明 编程小翁@博客园,邮件[email protected],欢迎各位与我在C/C++/Objective-C/机器视觉等领域展开交流!

=======================================================

  在上一篇专题文章【原】iOSCoreAnimation动画系列教程(一):CABasicAnimation【包会】中我们学习了iOS核心动画CoreAnimation中CABasicAnimation动画的使用方法。CABasicAnimation已经可以应付一些比较简单的应用场景了,比如view的平移出现、淡入淡出等。但是在有些情况下直线的运动并不能满足我们的需要,因此有必要学习进阶版的核心动画,那就是CAKeyFrameAnimation。

  在上一篇专题中我们提到,CAAnimation可分为以下四种:

1.CABasicAnimation
通过设定起始点,终点,时间,动画会沿着你这设定点进行移动。可以看做特殊的CAKeyFrameAnimation
2.CAKeyframeAnimation
Keyframe顾名思义就是关键点的frame,你可以通过设定CALayer的始点、中间关键点、终点的frame,时间,动画会沿你设定的轨迹进行移动
3.CAAnimationGroup
Group也就是组合的意思,就是把对这个Layer的所有动画都组合起来。PS:一个layer设定了很多动画,他们都会同时执行,如何按顺序执行我到时候再讲。
4.CATransition
这个就是苹果帮开发者封装好的一些动画,

CABasicAnimation算是CAKeyFrameAnimation的特殊情况,即不考虑中间变换过程,只考虑起始点与目标点就可以了。而CAKeyFrameAnimation则更复杂一些,允许我们在起点与终点间自定义更多内容来达到我们的实际应用需求!比如,手机淘宝中,当你添加物品到购物车后会出现将物品抛到购物车的效果,这种效果实现起来也不难,无非是先绘制抛物线在执行position以及scale的GroupAnimation而已,以下图1是我模仿该功能小玩出来的一个demo示例,感兴趣的话你可以自己实现一下试试:D.

                    

                        图1                                              图2

下面我们以实现“小圆球绕矩形跑道循环跑动”为目标开始对CAKeyFrameAnimation的介绍,如图2所示。小圆球的运动轨迹可分为四段,每段的运动速度不同,第一段中先慢后快再慢。先贴上源码方便后面分析:

 1 //绕矩形循环跑
 2 - (void)initRectLayer
 3 {
 4     rectLayer = [[CALayer alloc] init];
 5     rectLayer.frame = CGRectMake(15, 200, 30, 30);
 6     rectLayer.cornerRadius = 15;
 7     rectLayer.backgroundColor = [[UIColor blackColor] CGColor];
 8     [self.view.layer addSublayer:rectLayer];
 9     CAKeyframeAnimation *rectRunAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
10     //设定关键帧位置,必须含起始与终止位置
11     rectRunAnimation.values = @[[NSValue valueWithCGPoint:rectLayer.frame.origin],
12                                 [NSValue valueWithCGPoint:CGPointMake(320 - 15,
13                                                                       rectLayer.frame.origin.y)],
14                                 [NSValue valueWithCGPoint:CGPointMake(320 - 15,
15                                                                       rectLayer.frame.origin.y + 100)],
16                                 [NSValue valueWithCGPoint:CGPointMake(15, rectLayer.frame.origin.y + 100)],
17                                 [NSValue valueWithCGPoint:rectLayer.frame.origin]];
18     //设定每个关键帧的时长,如果没有显式地设置,则默认每个帧的时间=总duration/(values.count - 1)
19     rectRunAnimation.keyTimes = @[[NSNumber numberWithFloat:0.0], [NSNumber numberWithFloat:0.6],
20                                   [NSNumber numberWithFloat:0.7], [NSNumber numberWithFloat:0.8],
21                                   [NSNumber numberWithFloat:1]];
22     rectRunAnimation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
23                                          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear],
24                                          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear],
25                                          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
26     rectRunAnimation.repeatCount = 1000;
27     rectRunAnimation.autoreverses = NO;
28     rectRunAnimation.calculationMode = kCAAnimationLinear;
29     rectRunAnimation.duration = 4;
30     [rectLayer addAnimation:rectRunAnimation forKey:@"rectRunAnimation"];
31 }

图3

对CAKeyFrameAnimation的使用与CABasicAnimation大同小异,有些属性是共通的,因此小翁建议你先阅读上一篇文章。KeyFrame的意思是关键帧,所谓“关键”就是改变物体运动趋势的帧,在该点处物体将发生运动状态,比如矩形的四个角,抛物线的顶点等。因此,聪明的你应该知道了,在上述例子中共有5个关键帧(图3中的ABCDE)。上个关键帧到当前关键帧之间的路径与当前关键帧相联系,比如AB->B,我们可以对AB进行定义动画定义,而自定义要通过众多CAKeyFrameAnimation的属性达到目的。CAKeyFrameAnimation的使用中有以下主要的属性需要注意,有些属性可能比较绕比较难以理解,我会结合图片进行必要的说明。

(1)values属性

values属性指明整个动画过程中的关键帧点,例如上例中的A-E就是通过values指定的。需要注意的是,起点必须作为values的第一个值。

(2)path属性

作用与values属性一样,同样是用于指定整个动画所经过的路径的。需要注意的是,values与path是互斥的,当values与path同时指定时,path会覆盖values,即values属性将被忽略。例如上述例子等价于代码中values方式的path设置方式为:

1     CGMutablePathRef path = CGPathCreateMutable();
2     CGPathMoveToPoint(path, NULL, rectLayer.position.x - 15, rectLayer.position.y - 15);
3     CGPathAddLineToPoint(path, NULL, 320 - 15, rectLayer.frame.origin.y);
4     CGPathAddLineToPoint(path, NULL, 320 - 15, rectLayer.frame.origin.y + 100);
5     CGPathAddLineToPoint(path, NULL, 15, rectLayer.frame.origin.y + 100);
6     CGPathAddLineToPoint(path, NULL, 15, rectLayer.frame.origin.y);
7     rectRunAnimation.path = path;
8     CGPathRelease(path);

(3)keyTimes属性

该属性是一个数组,用以指定每个子路径(AB,BC,CD)的时间。如果你没有显式地对keyTimes进行设置,则系统会默认每条子路径的时间为:ti=duration/(5-1),即每条子路径的duration相等,都为duration的1\4。当然,我们也可以传个数组让物体快慢结合。例如,你可以传入{0.0, 0.1,0.6,0.7,1.0},其中首尾必须分别是0和1,因此tAB=0.1-0, tCB=0.6-0.1, tDC=0.7-0.6, tED=1-0.7.....

(4)timeFunctions属性

用过UIKit层动画的同学应该对这个属性不陌生,这个属性用以指定时间函数,类似于运动的加速度,有以下几种类型。上例子的AB段就是用了淡入淡出效果。记住,这是一个数组,你有几个子路径就应该传入几个元素

1 kCAMediaTimingFunctionLinear//线性
2 kCAMediaTimingFunctionEaseIn//淡入
3 kCAMediaTimingFunctionEaseOut//淡出
4 kCAMediaTimingFunctionEaseInEaseOut//淡入淡出
5 kCAMediaTimingFunctionDefault//默认

(5)calculationMode属性

该属性决定了物体在每个子路径下是跳着走还是匀速走,跟timeFunctions属性有点类似

1 const kCAAnimationLinear//线性,默认
2 const kCAAnimationDiscrete//离散,无中间过程,但keyTimes设置的时间依旧生效,物体跳跃地出现在各个关键帧上
3 const kCAAnimationPaced//平均,keyTimes跟timeFunctions失效
4 const kCAAnimationCubic//平均,同上
5 const kCAAnimationCubicPaced//平均,同上

关于CAKeyFrameAnimation的介绍基本结束了,在文章的最后,开源一个小翁封装的抛物动画代码,上文的图1就是在这份代码的基础上实现的:

 1 #import <Foundation/Foundation.h>
 2 #import <UIKit/UIKit.h>
 3 #import <QuartzCore/QuartzCore.h>
 4
 5 @protocol ThrowLineToolDelegate;
 6
 7 @interface ThrowLineTool : NSObject
 8
 9 @property (nonatomic, assign) id<ThrowLineToolDelegate>delegate;
10 @property (nonatomic, retain) UIView *showingView;
11
12 + (ThrowLineTool *)sharedTool;
13
14 /**
15  *  将某个view或者layer从起点抛到终点
16  *
17  *  @param obj    被抛的物体
18  *  @param start  起点坐标
19  *  @param end    终点坐标
20  *  @param height 高度,抛物线最高点比起点/终点y坐标最低(即高度最高)所超出的高度
21  */
22 - (void)throwObject:(UIView *)obj from:(CGPoint)start to:(CGPoint)end
23              height:(CGFloat)height duration:(CGFloat)duration;
24
25 @end
26
27
28 @protocol ThrowLineToolDelegate <NSObject>
29
30 /**
31  *  抛物线结束的回调
32  */
33 - (void)animationDidFinish;
34
35 @end

ThrowLineTool.h

 1 #import "ThrowLineTool.h"
 2
 3 static ThrowLineTool *s_sharedInstance = nil;
 4 @implementation ThrowLineTool
 5
 6 + (ThrowLineTool *)sharedTool
 7 {
 8     if (!s_sharedInstance) {
 9         s_sharedInstance = [[[self class] alloc] init];
10     }
11     return s_sharedInstance;
12 }
13
14 /**
15  *  将某个view或者layer从起点抛到终点
16  *
17  *  @param obj    被抛的物体
18  *  @param start  起点坐标
19  *  @param end    终点坐标
20  *  @param height 高度,抛物线最高点比起点/终点y坐标最低(即高度最高)所超出的高度
21  */
22 - (void)throwObject:(UIView *)obj from:(CGPoint)start to:(CGPoint)end
23              height:(CGFloat)height duration:(CGFloat)duration
24 {
25     self.showingView = obj;
26     //初始化抛物线path
27     CGMutablePathRef path = CGPathCreateMutable();
28     CGFloat cpx = (start.x + end.x) / 2;
29     CGFloat cpy = -height;
30     CGPathMoveToPoint(path, NULL, start.x, start.y);
31     CGPathAddQuadCurveToPoint(path, NULL, cpx, cpy, end.x, end.y);
32     CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
33     animation.path = path;
34     CFRelease(path);
35     CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
36     scaleAnimation.autoreverses = YES;
37     scaleAnimation.toValue = [NSNumber numberWithFloat:(CGFloat)((arc4random() % 4) + 4) / 10.0];
38
39     CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
40     groupAnimation.delegate = self;
41     groupAnimation.repeatCount = 1;
42     groupAnimation.duration = duration;
43     groupAnimation.removedOnCompletion = NO;
44     groupAnimation.animations = @[scaleAnimation, animation];
45     [obj.layer addAnimation:groupAnimation forKey:@"position scale"];
46 }
47
48 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
49 {
50     if (self.delegate && [self.delegate respondsToSelector:@selector(animationDidFinish)]) {
51         [self.delegate performSelector:@selector(animationDidFinish) withObject:nil];
52     }
53     self.showingView = nil;
54 }
55
56 - (void)dealloc
57 {
58     [super dealloc];
59 }
60
61 @end

ThrowLineTool.m

 1 - (void)beginThrowing:(UIView *)view
 2 {
 3     ThrowLineTool *tool = [ThrowLineTool sharedTool];
 4     tool.delegate = self;
 5     UIImageView *bagImgView = (UIImageView *)[self viewWithTag:1000];
 6     CGFloat startX = 0;//arc4random() % (NSInteger)CGRectGetWidth(self.frame);
 7     CGFloat startY = 150;//CGRectGetHeight(self.frame);
 8     CGFloat endX = CGRectGetMidX(bagImgView.frame) + 10 - (arc4random() % 50);
 9     CGFloat endY = CGRectGetMidY(bagImgView.frame);
10     CGFloat height = 50 + arc4random() % 40;
11    [tool throwObject:view
12                 from:CGPointMake(startX, startY)
13                   to:CGPointMake(endX, endY)
14               height:height duration:1.6];
15 }

使用方法

时间: 2024-08-24 08:40:40

【原】iOSCoreAnimation动画系列教程(二):CAKeyFrameAnimation【包会】的相关文章

【原】Github系列之二:开源 支持多种形式多种动画风格的推送小红点WZLBadge(iOS)

概述 今天我们来实现一个在iOS中让人又爱又恨的推送“小红点”WZLBadge.那什么是badge呢?当后台有数据更新需要让用户知道时,在按钮或者其他控件上显示一个“小红点”提醒用户.注意,这里的“小红点”仅仅是泛指,实际的视图可以天马行空,在这个版本中我们先实现以下几种: 小红点 红底白字“new” 红底白字数字 为了让小红点显示后更加醒目,在这个版本中我又实现了三种不同的状态动画(status animation): 心脏跳动效果(WBadgeAnimTypeScale) 呼吸灯效果(WBa

C#微信公众号开发系列教程二(新手接入指南)

http://www.cnblogs.com/zskbll/p/4093954.html 此系列前面已经更新了两篇博文了,都是微信开发的前期准备工作,现在切入正题,本篇讲解新手接入的步骤与方法,大神可直接跳过,也欢迎大神吐槽. 目录 C#微信公众号开发系列教程一(调试环境部署) C#微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南) 微信公众平台消息接口的工作原理大概可以这样理解:从用户端到公众号端一个流程是这样的,用户发送消息到微信服务器,微

CRL快速开发框架系列教程二(基于Lambda表达式查询)

本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框架系列教程四(删除数据) CRL快速开发框架系列教程五(使用缓存) CRL快速开发框架系列教程六(分布式缓存解决方案) CRL快速开发框架系列教程七(使用事务) CRL快速开发框架系列教程八(使用CRL.Package) CRL快速开发框架系列教程九(导入/导出数据) CRL快速开发框架系列教程十(

Shell脚本系列教程二: 开始Shell编程

Shell脚本系列教程二: 开始Shell编程 2.1 如何写shell script? (1) 最常用的是使用vi或者mcedit来编写shell脚本, 但是你也可以使用任何你喜欢的编辑器; (2) 脚本写好之后, 要给脚本设置可执行权限: 语法为: chmod  [option]  mode  script-name $ chmod +x script-name # 对所有用户(a, 默认) $ chmod 775 script-name 这里, 775这3个数字分别表示此文件对于用户(u)

kettle系列教程二

 kettle系列教程二 1.Hello World 示例 通过这个例子,介绍kettle的一些基础知识及应用: n 使用Spoon工具 n 转换的创建 n 步骤及连线 n 预定义变量 n 在Spoon中预览和执行 n 使用pan工具在终端窗口执行转换 概述 我们要实现的目标是:通过一个包含人名称的CSV文件,创建一个XML文件,内容是针对每个人添加上问候. 如果csv文件内容如下: last_name, name Suarez,Maria Guimaraes,Joao Rush,Jenni

ComicEnhancerPro 系列教程二十:用“文件比较”看有损、无损

作者:马健邮箱:[email protected] 主页:http://www.comicer.com/stronghorse/ 发布:2017.07.23 教程二十:用"文件比较"看有损.无损 在前两篇教程中,扯了一大堆JPG文件的有损压缩,平时在网上也经常看到一些人整天把有损.无损挂在嘴边,但具体是否真的无损?有损又损了多少.损在哪里?讲得清的人就真心不多了.CEP从v4.13开始提供"文件比较"功能,可用于定量比较有损.无损. 按照CEP的使用说明,"

Influx Sql系列教程二:retention policy 保存策略

retention policy这个东西相比较于传统的关系型数据库(比如mysql)而言,是一个比较新的东西,在将表之前,有必要来看一下保存策略有什么用,以及可以怎么用 I. 基本操作 1. 创建retention policy retention policy依托于database存在,也就是说保存策略创建时,需要指定具体的数据库,语法如下 CREATE RETENTION POLICY <retention_policy_name> ON <database_name> DUR

Cobaltstrike系列教程(二)-Listner与Payload生成 heatlevel

0x000-前文 Cobaltstrike系列教程(一)简介与安装 0x001-Listner(监听器)介绍 ①Cobaltstrike listner简介 可能有一些小白并不理解什么叫做listner,在此科普一下.Listner(监听器):专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动.在许多的渗透测试工具中,例如empire,metasploit,cobaltstrike中都有listner的存在.大家可以简单地理解为lis

【原】iOS动画系列教程(一):CoreAnimation动画形象教程【包会】

在iOS中,图形可分为以下几个层次: 越上层,封装程度越高,动画实现越简洁越简单,但是自由度越低:反之亦然.本文着重介绍Core Animation层的基本动画实现方案. 在iOS中,展示动画可以类比于显示生活中的“拍电影”.拍电影有三大要素:演员+剧本+开拍,概念类比如下: 演员--->CALayer,规定电影的主角是谁 剧本--->CAAnimation,规定电影该怎么演,怎么走,怎么变换 开拍--->AddAnimation,开始执行 一.概念介绍 1.1CALayer是什么呢?