一:CAPropertyAnimation基本概念
CAPropertyAnimation提供了如下方法来创建属性动画。
+ (instancetype)animationWithKeyPath:(NSString *)path;该方法仅需要一个参数,该参数只是一个字符串的值,指定CALayer的动画属性名,该设置属性动画控制CALayer的哪个动画属性持续的改变。
除此之外,CAPropertyAnimation还支持如下属性:
keyPath:该属性值返回创建CAPropertyAnimation时指定的参数。
additive:该属性指定该属性动画是否以当前动画效果为基础。
cumulative:该属性指定动画是否为累加效果。
valueFunction:该属性值是一个CAValueFunction对象,该对象负责对属性改变的插值计算,系统已经提供了默认的插值计算方式,因此一般无须指定该属性。
如果要控制CALayer的位移动画,直接使用属性动画控制CALayer的position持续改变即可。如果要控制该CALayer的缩放,旋转,斜切等效果,则需要以下属性:
affineTransform:该属性值指定一个(- (CGAffineTransform)affineTransform;)CGAffineTransform对象(变换矩阵),该对象代表CALayer执行X,Y两个维度(也就是平面)上的旋转,缩放,位移,斜切,镜像等变换矩阵。
transform:该属性值指定一个CATransform3D对象,该对象代表对CALayer执行X,Y,Z三个维度(三维空间)中的旋转,缩放,位移,斜切,镜像等变换矩阵。很明显如果只是对CALayer进行平面上的变换,指定普通的affineTransform属性即可,如果要对CALayer执行三维空间的变化,则需要指定transform属性。
CATransform3D:就是下面的结构体
struct CATransform3D
{
CGFloat m11, m12, m13, m14;
CGFloat m21, m22, m23, m24;
CGFloat m31, m32, m33, m34;
CGFloat m41, m42, m43, m44;
};
其中(m11, m12, m13,m21, m22, m23,m31, m32, m33)将会组成变换矩阵,m14,m24,m34,m44只是占位符,通常m14,m24,m34会设置为0.m14设置为1.假如变换前的店坐标为(x,y,z),与该矩阵相乘后得到变换后该点的坐标。按矩阵相乘算法:
[x,y,z].(m11,m12,m13
m21,m22,m23=(x*m11+y*m21+z*m31 x*m12+y*m22+z*m32 x*m13+y*m23+z*m33)
m31,m32,m33)
上面公司计算出来的坐标还要加上tx,ty,tz这3个X,Y,Z方向的偏移量。因此对于点(x,y,z)经过CATransform3D变换后,该点的实际坐标为(x*m11+y*m21+z*m31+tx x*m12+y*m22+z*m32+ty x*m13+y*m23+z*m33+tz).
一般来说可以使用 Core Animation提供的如下函数来创建三维变换矩阵。
CATransform3DIsIdentity(CATransform3D t) :判断t矩阵是否为单位矩阵
CATransform3DEqualToTransform(CATransform3D a, CATransform3D b) :判断两个变换矩阵是否相等
CATransform3DMakeTranslation(CGFloat tx, CGFloat ty, CGFloat tz) :创建在x方向上移动tx,在y方向上移动ty,在z方向上移动tz的变换矩阵。
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz) :创建在x方向上缩放tx,在y方向上缩放ty,在z方向上缩放tz的变换矩阵。
CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z) :创建基于指定旋转轴旋转angle弧度的变换,其中x,y,z用于确定旋转轴的方向。比如 (1,0,0)指定旋转轴为x轴,(1,1,0)指定以x,y轴夹角的中线为旋转轴。
CATransform3DTranslate(CATransform3D t, CGFloat tx, CGFloat ty, CGFloat tz) :以已有t变换矩阵为基础进行位移变换。
CATransform3DScale(CATransform3D t, CGFloat sx, CGFloat sy, CGFloat sz) :
以已有t变换矩阵为基础进行缩放变换。
CATransform3DRotate(CATransform3D t, CGFloat angle, CGFloat x, CGFloat y, CGFloat z) :以已有t变换矩阵为基础进行旋转变换。
CATransform3DConcat(CATransform3D a, CATransform3D b) :对a变换矩阵进行累加
CATransform3DInvert(CATransform3D t) :对已有的t变换矩阵执行反转。
CATransform3DMakeAffineTransform(CGAffineTransform m) :将CGAffineTransform矩阵包装成为CATransform3D变换矩阵,该CATransform3D也只有x,y维度变换。
CATransform3DIsAffine(CATransform3D t) :如果t变换矩阵只有一个CGAffineTransform矩阵,则改函数返回YES.
CATransform3DGetAffineTransform(CATransform3D t) :获取t变换矩阵所包含的CGAffineTransform变换矩阵。
二:CAPropertyAnimation的使用
1:利用+ (instancetype)animationWithKeyPath:(NSString *)keyPath类方法创建属性动画
2:如果使用基本属性动画CABasicAnimation,则可指定fromValue,toValue两个属性值,其中fromValue指定动画属性开始时的属性值,toValue指定动画属性结束的属性值.如果使用CAKeyframeAnimation属性动画,则指定values属性值,该属性值是一个 NSArray属性,其中第一个元素是属性的开始值.依次变化.区别在于CABasicAnimation只能够指定开始值和结束值.而CAKeyframeAnimation则可以指定动画属性的多个值.CAKeyframeAnimation还可以通过控制路劲来控制移动.
3:调用 CALayer的- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key添加动画即可.key用于多图层进行分辨.单图层看不用设置.
三:demo 实现基本的位移 缩放 旋转 组合动画
#import <QuartzCore/QuartzCore.h>
#import "FKViewController.h"
@implementation FKViewController
CALayer *imageLayer;
- (void)viewDidLoad{
[superviewDidLoad];
//创建一个CALayer对象
imageLayer = [CALayerlayer];
//设置该CALayer的边框、大小、位置等属性
imageLayer.cornerRadius =6;
imageLayer.borderWidth =1;
imageLayer.borderColor = [UIColor blackColor].CGColor;
imageLayer.masksToBounds =YES;
imageLayer.frame =CGRectMake(30,30,100, 135);
//设置该imageLayer显示的图片
imageLayer.contents = (id)[[UIImage imageNamed:@"android"]CGImage];
[self.view.layer addSublayer:imageLayer];
NSArray* bnTitleArray = [NSArray arrayWithObjects:@"位移",@"旋转" ,@"缩放" ,@"动画组" ,nil];
//获取屏幕的内部高度
CGFloat totalHeight = [UIScreen mainScreen].bounds.size.height;
NSMutableArray* bnArray = [[NSMutableArray alloc]init];
//采用循环创建4个按钮
for(int i =0 ; i < 4 ; i++){
UIButton* bn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
bn.frame =CGRectMake(5 + i *80, totalHeight - 45 - 20 , 70 ,35);
[bnsetTitle:[bnTitleArrayobjectAtIndex:i] forState:UIControlStateNormal];
[bnArray addObject:bn];
[self.viewaddSubview:bn];
}
//为4个按钮绑定不同的事件处理方法
[[bnArray objectAtIndex:0] addTarget:self action:@selector(move:) forControlEvents:UIControlEventTouchUpInside]; [[bnArray objectAtIndex:1] addTarget:self action:@selector(rotate:) forControlEvents:UIControlEventTouchUpInside];
[[bnArray objectAtIndex:2] addTarget:self action:@selector(scale:) forControlEvents:UIControlEventTouchUpInside];
[[bnArray objectAtIndex:3] addTarget:self action:@selector(group:) forControlEvents:UIControlEventTouchUpInside];
}
-(void) move:(id)sender
{
CGPoint fromPoint =imageLayer.position;
CGPoint toPoint =CGPointMake(fromPoint.x +80 , fromPoint.y);
//创建不断改变CALayer的position属性的属性动画
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"position"];
//设置动画开始的属性值
anim.fromValue = [NSValue valueWithCGPoint:fromPoint];
//设置动画结束的属性值
anim.toValue = [NSValue valueWithCGPoint:toPoint];
anim.duration =0.5;
imageLayer.position = toPoint;//设置移动后图片的位置。
anim.removedOnCompletion =YES;
//为imageLayer添加动画
[imageLayer addAnimation:animforKey:nil];
}
-(void) rotate:(id)sender
{
//创建不断改变CALayer的transform属性的属性动画
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"transform"];
CATransform3D fromValue =imageLayer.transform;
//设置动画开始的属性值
anim.fromValue = [NSValue valueWithCATransform3D:fromValue];
//绕X轴旋转180度
CATransform3D toValue =CATransform3DRotate(fromValue,M_PI , 1 , 0 , 0);
//绕Y轴旋转180度
// CATransform3D toValue = CATransform3DRotate(fromValue, M_PI , 0 , 1 , 0);
// // 绕Z轴旋转180度
// CATransform3D toValue = CATransform3DRotate(fromValue, M_PI , 0 , 0 , 1);
//设置动画结束的属性值
anim.toValue = [NSValue valueWithCATransform3D:toValue];
anim.duration =0.5;
imageLayer.transform = toValue;
anim.removedOnCompletion =YES;
//为imageLayer添加动画
[imageLayer addAnimation:animforKey:nil];
}
-(void) scale:(id)sender
{
//创建不断改变CALayer的transform属性的属性动画
CAKeyframeAnimation* anim = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
//设置CAKeyframeAnimation控制transform属性依次经过的属性值
anim.values = [NSArrayarrayWithObjects:
[NSValue valueWithCATransform3D:imageLayer.transform],
[NSValue valueWithCATransform3D:CATransform3DScale(imageLayer.transform ,0.2,0.2,1)],
[NSValue valueWithCATransform3D:CATransform3DScale(imageLayer.transform,2,2 ,1)],
[NSValue valueWithCATransform3D:imageLayer.transform],nil];
anim.duration =5;
anim.removedOnCompletion =YES;
//为imageLayer添加动画
[imageLayer addAnimation:animforKey:nil];
}
-(void) group:(id)sender
{
CGPoint fromPoint =imageLayer.position;
CGPoint toPoint =CGPointMake(280 , fromPoint.y +300);
//创建不断改变CALayer的position属性的属性动画
CABasicAnimation* moveAnim = [CABasicAnimation animationWithKeyPath:@"position"];
//设置动画开始的属性值
moveAnim.fromValue = [NSValue valueWithCGPoint:fromPoint];
//设置动画结束的属性值
moveAnim.toValue = [NSValue valueWithCGPoint:toPoint];
moveAnim.removedOnCompletion =YES;
//创建不断改变CALayer的transform属性的属性动画
CABasicAnimation* transformAnim = [CABasicAnimation animationWithKeyPath:@"transform"];
CATransform3D fromValue =imageLayer.transform;
//设置动画开始的属性值
transformAnim.fromValue = [NSValue valueWithCATransform3D: fromValue];
//创建缩放为X、Y两个方向上缩放为0.5的变换矩阵
CATransform3D scaleValue =CATransform3DScale(fromValue , 0.5 , 0.5,1);
//绕Z轴旋转180度的变换矩阵
CATransform3D rotateValue =CATransform3DRotate(fromValue,M_PI , 0 , 0 , 1);
//计算两个变换矩阵的和
CATransform3D toValue =CATransform3DConcat(scaleValue, rotateValue);
//设置动画技术的属性值
transformAnim.toValue = [NSValuevalueWithCATransform3D:toValue];
//动画效果累加
transformAnim.cumulative =YES;
//动画重复执行2次,旋转360度
transformAnim.repeatCount =2;
transformAnim.duration =3;
//位移、缩放、旋转组合起来执行
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArrayarrayWithObjects:moveAnim, transformAnim ,nil];
animGroup.duration =6;
//为imageLayer添加动画
[imageLayer addAnimation:animGroupforKey:nil];
}
@end
四:控制移动的路径
对于CAKeyframeAnimation而言,它除了可通过values属性指定动画过程中的多个值之外,还可以通过path属性指定CALayer的移动路径,改属性就是CGPathRef,通过这种方式即可控制CALayer按我们指定的轨迹移动,从而执行更加细致的动画。
#import <QuartzCore/QuartzCore.h>
#import "FKViewController.h"
@interfaceFKViewController ()
@end
@implementation FKViewController
CALayer* fishLayer;
NSInteger fishFrame;
NSTimer* timer;
// 定义NSMutableArray装鱼的10个动画帧
NSMutableArray* fishFrameArray;
- (void)viewDidLoad
{
[superviewDidLoad];
//创建CALayer作为背景
CALayer* bg = [CALayerlayer];
//设置背景图片
bg.contents = (id)[UIImageimageNamed:@"bg.jpg"].CGImage;
bg.contentsGravity =kCAGravityCenter;
bg.frame =CGRectMake(0,0,320, 480);
[self.view.layer addSublayer:bg];
fishFrameArray = [[NSMutableArray alloc]init];
//初始化鱼的10个动画帧,并添加到fishFrameArray集合中
for(int i =0 ; i < 10 ; i++){
[fishFrameArray addObject:[UIImage imageNamed:[NSString stringWithFormat:@"fish%d.png" , i]]];//多张连串的图片
}
//创建定时器控制小鱼的动画帧的改变。
timer= [NSTimer scheduledTimerWithTimeInterval:0.15
target:self
selector:@selector(change)
userInfo:nil
repeats:YES];
//创建CALayer
fishLayer = [CALayer layer];
//设置CALayer显示内容的对齐、缩放模式(不缩放,直接显示在中间)
fishLayer.contentsGravity =kCAGravityCenter;
//设置fishLayer的大小
fishLayer.frame =CGRectMake(128,6,90, 40);
[self.view.layeraddSublayer:fishLayer];
//创建一个按钮,通过该按钮触发小鱼的游动
UIButton* bn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
bn.frame =CGRectMake(10 ,10 , 60 ,35);
[bnsetTitle:@"开始"forState:UIControlStateNormal];
[self.viewaddSubview:bn];
//用户点击按钮时,激发start:方法
[bn addTarget:self action:@selector(start:) forControlEvents:UIControlEventTouchUpInside];
}
-(void) start:(id)sender
{
//创建对CALayer的position属性执行控制的属性动画
CAKeyframeAnimation* anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
//创建路径
CGMutablePathRef movePath =CGPathCreateMutable();
//添加一条圆形的路径
CGPathAddArc(movePath,nil,170, 175, 150, -M_PI /2,M_PI * 3 / 2, YES);
//设置anim动画的移动路径
anim.path = movePath;
//创建对CALayer的transform属性执行控制的属性动画
CAKeyframeAnimation* anim2 = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
//指定关键帧动画的3个关键值:分别是不旋转,旋转180度,旋转360度
anim2.values = [NSArray arrayWithObjects: [NSValue valueWithCATransform3D:CATransform3DIdentity],
[NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI , 0,0,1)],
[NSValue valueWithCATransform3D:CATransform3DMakeRotation(2 * M_PI ,0,0,1)],nil];
//使用动画组来组合2个动画
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:anim, anim2, nil];
//指定动画重复10次
animGroup.repeatCount =10;
animGroup.duration =24;
//为fishLayer添加动画
[fishLayer addAnimation:animGroupforKey:@"move"];
}
// 该方法由定时器触发,不断更改fishLayer显示的动画帧
- (void) change
{
fishLayer.contents = (id)[[fishFrameArray
objectAtIndex:fishFrame++ %10]CGImage];
}
@end