一、简介
IOS 动画主要是指Core Animation框架。官方使用文档地址为:Core Animation Guide。
Core Animation是IOS和OS X平台上负责图形渲染与动画的基础框架。Core Animation可以作用与动画视图或者其他可视元素,为你完成了动画所需的大部分绘帧工作。你只需要配置少量的动画参数(如开始点的位置和结束点的位置)即可使用Core Animation的动画效果。Core Animation将大部分实际的绘图任务交给了图形硬件来处理,图形硬件会加速图形渲染的速度。这种自动化的图形加速技术让动画拥有更高的帧率并且显示效果更加平滑,不会加重CPU的负担而影响程序的运行速度。
二、Core Animation类图以及常用字段
Core Animation类的继承关系图
常用属性 duration : 动画的持续时间 beginTime : 动画的开始时间 repeatCount : 动画的重复次数 autoreverses : 执行的动画按照原动画返回执行 timingFunction : 控制动画的显示节奏系统提供五种值选择,分别是:
- kCAMediaTimingFunctionLinear 线性动画
- kCAMediaTimingFunctionEaseIn 先慢后快(慢进快出)
- kCAMediaTimingFunctionEaseOut 先块后慢(快进慢出)
- kCAMediaTimingFunctionEaseInEaseOut 先慢后快再慢
- kCAMediaTimingFunctionDefault 默认,也属于中间比较快
delegate : 动画代理。能够检测动画的执行和结束。
/* Delegate methods for CAAnimation. */
@interface NSObject (CAAnimationDelegate)
/* Called when the animation begins its active duration. */
- (void)animationDidStart:(CAAnimation *)anim;
/* Called when the animation either completes its active duration or
* is removed from the object it is attached to (i.e. the layer). 'flag'
* is true if the animation reached the end of its active duration
* without being removed. */
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
@end
path:关键帧动画中的执行路径 type : 过渡动画的动画类型,系统提供了四种过渡动画。
- kCATransitionFade 渐变效果
- kCATransitionMoveIn 进入覆盖效果
- kCATransitionPush 推出效果
- kCATransitionReveal 揭露离开效果 subtype : 过渡动画的动画方向
- kCATransitionFromRight 从右侧进入
- kCATransitionFromLeft 从左侧进入
- kCATransitionFromTop 从顶部进入
- kCATransitionFromBottom 从底部进入
三、IOS动画的调用方式
第一种:UIView 代码块调用
_demoView.frame = CGRectMake(0, SCREEN_HEIGHT/2-50, 50, 50);
[UIView animateWithDuration:1.0f animations:^{
_demoView.frame = CGRectMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-50, 50, 50);
} completion:^(BOOL finished) {
_demoView.frame = CGRectMake(SCREEN_WIDTH/2-25, SCREEN_HEIGHT/2-50, 50, 50);
}];
第二种:UIView [begin commit]模式
[UIView beginAnimations:@"move" context:nil];
[UIView setAnimationDuration:2];
[UIView setAnimationDelegate:self];
//改变它的frame的x,y的值
animationView.frame = CGRectMake(100, 100, 120, 100);
[UIView commitAnimations];
第三种:使用Core Animation中的类
<pre name="code" class="objc"> // time:闪烁间隔 repeatCount = MAXFLOAT(永久闪烁)
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
//这是透明度值得变化。
animation.fromValue = [NSNumber numberWithFloat:1.0f];
animation.toValue = [NSNumber numberWithFloat:0.0f];
//是否自动倒退
animation.autoreverses = YES;
animation.duration = time;
animation.repeatCount = repeatCount;
//动画执行完毕之后不删除动画
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
//没有的话是均匀的动画。
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
四、iOS动画的使用
4.1:基础动画(CABaseAnimation)
重要属性 fromValue : keyPath对应的初始值 toValue : keyPath对应的结束值基础动画主要提供了对于CALayer对象中的可变属性进行简单动画的操作。比如:位移、透明度、缩放、旋转、背景色等等。 效果演示:
位移动画代码演示:
//使用CABasicAnimation创建基础动画
CABasicAnimation *anima = [CABasicAnimation animationWithKeyPath:@"position"];
anima.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, SCREEN_HEIGHT/2-75)];
anima.toValue = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-75)];
anima.duration = 1.0f;
//anima.fillMode = kCAFillModeForwards;
//anima.removedOnCompletion = NO;
[_demoView.layer addAnimation:anima forKey:@"positionAnimation"];
注意点 如果fillMode=kCAFillModeForwards和removedOnComletion=NO,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变。
4.2:关键帧动画(CAKeyframeAnimation)
CAKeyframeAnimation和CABaseAnimation都属于CAPropertyAnimatin的子类。CABaseAnimation只能从一个数值(fromValue)变换成另一个数值(toValue),而CAKeyframeAnimation则会使用一个NSArray保存一组关键帧。重要属性values : 就是上述的NSArray对象。里面的元素称为”关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
path : 可以设置一个CGPathRef\CGMutablePathRef,让层跟着路径移动。path只对CALayer的anchorPoint和position起作用。如果你设置了path,那么values将被忽略。
keyTimes : 可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧.当keyTimes没有设置的时候,各个关键帧的时间是平分的。
效果演示:
圆形路径动画代码演示:
CAKeyframeAnimation *anima = [CAKeyframeAnimation animationWithKeyPath:@"position"];
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(SCREEN_WIDTH/2-100, SCREEN_HEIGHT/2-100, 200, 200)];
anima.path = path.CGPath;
anima.duration = 2.0f;
[_demoView.layer addAnimation:anima forKey:@"pathAnimation"];
说明
:CABasicAnimation可看做是最多只有2个关键帧的CAKeyframeAnimation
4.3:组动画(CAAnimationGroup)
CAAnimation的子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行。重要属性 animations : 用来保存一组动画对象的NSArray
效果演示:
CAKeyframeAnimation *anima1 = [CAKeyframeAnimation animationWithKeyPath:@"position"];
NSValue *value0 = [NSValue valueWithCGPoint:CGPointMake(0, SCREEN_HEIGHT/2-50)];
NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2-50)];
NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2+50)];
NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2+50)];
NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2-50)];
NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-50)];
anima1.values = [NSArray arrayWithObjects:value0,value1,value2,value3,value4,value5, nil];
//缩放动画
CABasicAnimation *anima2 = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
anima2.fromValue = [NSNumber numberWithFloat:0.8f];
anima2.toValue = [NSNumber numberWithFloat:2.0f];
//旋转动画
CABasicAnimation *anima3 = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
anima3.toValue = [NSNumber numberWithFloat:M_PI*4];
//组动画
CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.animations = [NSArray arrayWithObjects:anima1,anima2,anima3, nil];
groupAnimation.duration = 4.0f;
[_demoView.layer addAnimation:groupAnimation forKey:@"groupAnimation"];
4.4:过渡动画(CATransition)
CAAnimation的子类,用于做过渡动画或者转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。 重要属性 type:动画过渡类型
Apple 官方的SDK其实只提供了四种过渡效果。
- kCATransitionFade 渐变效果
- kCATransitionMoveIn 进入覆盖效果
- kCATransitionPush 推出效果
- kCATransitionReveal 揭露离开效果 私有API提供了其他很多非常炫的过渡动画,比如@"cube"、@"suckEffect"、@"oglFlip"、 @"rippleEffect"、@"pageCurl"、@"pageUnCurl"、@"cameraIrisHollowOpen"、@"cameraIrisHollowClose"等。 注意点 私有api,不建议开发者们使用。因为苹果公司不提供维护,并且有可能造成你的app审核不通过。
subtype:动画过渡方向
- kCATransitionFromRight 从右侧进入
- kCATransitionFromLeft 从左侧进入
- kCATransitionFromTop 从顶部进入
- kCATransitionFromBottom 从底部进入
startProgress:动画起点(在整体动画的百分比) endProgress:动画终点(在整体动画的百分比)
效果演示:4.5:综合案例
4.5.1 : 仿Path菜单效果
效果演示:4.5.2: 仿钉钉菜单效果
效果演示:4.5.3: 点赞烟花效果动画
效果演示:五、总结
任何复杂的动画其实都是由一个个简单的动画组装而成的,只要我们善于分解和组装,我们就能实现出满意的效果。动画其实也不是那么难。六、常用动画代码
以下是自己封装了一些常用动画管理类//
// AnimationManager.h
// redlips
//
// Created by zizp on 15/12/23.
// Copyright © 2015年 xiaohongchun. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface AnimationManager : NSObject
/* 闪烁动画
* time:闪烁间隔
* repeatCount = MAXFLOAT(永久闪烁)重复次数
*/
+ (CABasicAnimation *)opacityForever_Animation:(CGFloat)time repeatCount:(CGFloat)repeatCount;
/* 横向、纵向移动
* time:
* x,y : 终点
*/
+ (CABasicAnimation *)moveX:(CGFloat)time X:(NSNumber *)x;
+ (CABasicAnimation *)moveY:(CGFloat)time Y:(NSNumber *)y;
/* 缩放
* time:
* Multiple: fromValue
* orginMultiple: toValue
* repeatTimes: 重复次数
*/
+ (CABasicAnimation *)scale:(NSNumber *)Multiple orgin:(NSNumber *)orginMultiple durTimes:(CGFloat)time Rep:(CGFloat)repertTimes;
/* 组合动画
* time:
* animationAry: 动画组
* repeatTimes: 重复次数
*/
+ (CAAnimationGroup *)groupAnimation:(NSArray *)animationAry durTimes:(CGFloat)time Rep:(CGFloat)repeatTimes;
/* 路径动画
* path: 路径
* time:
* repeatTimes: 重复次数
*/
+ (CAKeyframeAnimation *)keyframeAnimation:(CGMutablePathRef)path durTimes:(CGFloat)time Rep:(CGFloat)repeatTimes;
/* 旋转动画
* time:
* degree: 角度
* direction: 方向
* repeatTimes: 重复次数
*/
+ (CABasicAnimation *)rotation:(CGFloat)time degree:(CGFloat)degree direction:(CGFloat)direction repeatCount:(CGFloat)repeatCount;
@end
//
// AnimationManager.m
// redlips
//
// Created by zizp on 15/12/23.
// Copyright © 2015年 xiaohongchun. All rights reserved.
//
#import "AnimationManager.h"
static NSString * const kAnimationWithKeyPathRotation_X = @"transform.rotation.x"; /**< The rotation, in radians, in the x axis. */
static NSString * const kAnimationWithKeyPathRotation_Y = @"transform.rotation.y"; /**< The rotation, in radians, in the y axis. */
static NSString * const kAnimationWithKeyPathRotation_Z = @"transform.rotation.z"; /**< The rotation, in radians, in the z axis. */
static NSString * const kAnimationWithKeyPathRotation = @"transform.rotation"; /**< The rotation, in radians, in the z axis. This is identical to setting the rotation.z field */
static NSString * const kAnimationWithKeyPathScale_X = @"transform.scale.x"; /**< Scale factor for the x axis. */
static NSString * const kAnimationWithKeyPathScale_Y = @"transform.scale.y"; /**< Scale factor for the y axis. */
static NSString * const kAnimationWithKeyPathScale_Z = @"transform.scale.z"; /**< Scale factor for the z axis. */
static NSString * const kAnimationWithKeyPathScale = @"transform.scale"; /**< Average of all three scale factors */
static NSString * const kAnimationWithKeyPathTranslation_X = @"transform.translation.x";/**< Translate in the x axis */
static NSString * const kAnimationWithKeyPathTranslation_Y = @"transform.translation.y";/**< Translate in the y axis */
static NSString * const kAnimationWithKeyPathTranslation_Z = @"transform.translation.z";/**< Translate in the z axis */
static NSString * const kAnimationWithKeyPathTranslation = @"transform.translation"; /**< Translate in the x and y axis. Value is an CGPoint*/
static NSString * const kAnimationWithKeyPathOpacity = @"opacity"; /**< 闪烁 */
static NSString * const kAnimationWithKeyPathPosition = @"position"; /**< 路径 */
static NSString * const kAnimationWithKeyPathTransform = @"transform"; /**< 旋转 */
@implementation AnimationManager
#pragma mark ====== 闪烁的动画 ======
+ (CABasicAnimation *)opacityForever_Animation:(CGFloat)time repeatCount:(CGFloat)repeatCount
{
// time:闪烁间隔 repeatCount = MAXFLOAT(永久闪烁)
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
//这是透明度值得变化。
animation.fromValue = [NSNumber numberWithFloat:1.0f];
animation.toValue = [NSNumber numberWithFloat:0.0f];
//是否自动倒退
animation.autoreverses = YES;
animation.duration = time;
animation.repeatCount = repeatCount;
//动画执行完毕之后不删除动画
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
//没有的话是均匀的动画。
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
return animation;
}
#pragma mark ====== 横向、纵向移动 ======
+ (CABasicAnimation *)moveX:(CGFloat)time X:(NSNumber *)x
{
// .y 的话就向下移动。
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x" ];
animation.toValue = x;
animation.duration = time;
//是否删除动画(若为 YES 则动画完成后返回设定的 frame)
animation.removedOnCompletion = NO;
animation.repeatCount = MAXFLOAT ;
animation.fillMode = kCAFillModeForwards ;
return animation;
}
+ (CABasicAnimation *)moveY:(CGFloat)time Y:(NSNumber *)y
{
// .y 的话就向下移动。
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y" ];
animation.toValue = y;
animation.duration = time;
//是否删除动画(若为 YES 则动画完成后返回设定的 frame)
animation.removedOnCompletion = NO;
animation.repeatCount = MAXFLOAT ;
animation.fillMode = kCAFillModeForwards ;
return animation;
}
#pragma mark ====== 缩放 ======
+ (CABasicAnimation *)scale:(NSNumber *)Multiple orgin:(NSNumber *)orginMultiple durTimes:(CGFloat)time Rep:(CGFloat)repertTimes
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale" ];
animation.fromValue = Multiple;
animation.toValue = orginMultiple;
//是否自动倒退
animation.autoreverses = YES;
animation.repeatCount = repertTimes;
animation.duration = time; // 不设置时候的话,有一个默认的缩放时间.
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
return animation;
}
#pragma mark ====== 组合动画 ======
+ (CAAnimationGroup *)groupAnimation:(NSArray *)animationAry durTimes:(CGFloat)time Rep:(CGFloat)repeatTimes
{
CAAnimationGroup *animation = [CAAnimationGroup animation];
animation.animations = animationAry;
animation.duration = time;
animation.removedOnCompletion = NO;
animation.repeatCount = repeatTimes;
animation.fillMode = kCAFillModeForwards;
return animation;
}
#pragma mark ====== 路径动画 ======
+ (CAKeyframeAnimation *)keyframeAnimation:(CGMutablePathRef)path durTimes:(CGFloat)time Rep:(CGFloat)repeatTimes
{
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
animation.path = path;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
//是否自动倒退
animation.autoreverses = NO;
animation.duration = time;
animation.repeatCount = repeatTimes;
return animation;
}
#pragma mark ====== 旋转动画 ======
+ (CABasicAnimation *)rotation:(CGFloat)time degree:(CGFloat)degree direction:(CGFloat)direction repeatCount:(CGFloat)repeatCount
{
CATransform3D rotationTransform = CATransform3DMakeRotation(degree, 0, 0, direction);
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
animation.toValue = [NSValue valueWithCATransform3D:rotationTransform];
animation.duration = time;
//是否自动倒退
animation.autoreverses = NO;
animation.cumulative = NO;
animation.fillMode = kCAFillModeForwards;
animation.repeatCount = repeatCount;
animation.delegate = self;
return animation;
}
#pragma mark ====== 粒子动画 ======
+ (CAEmitterLayer *)AnimationEmitterPosition:(CGPoint)emitterPosition
{
CAEmitterLayer *fireworksEmitter = [CAEmitterLayer layer];
//发射位置
fireworksEmitter.emitterPosition = emitterPosition;
//发射源的z坐标位置
fireworksEmitter.emitterZPosition = -20.f;
//发射源的大小
fireworksEmitter.emitterSize = CGSizeMake(10.f, 10.f);
//决定粒子形状的深度联系
//fireworksEmitter.emitterDepth = 30.f;
//发射模式
fireworksEmitter.emitterMode = kCAEmitterLayerOutline;
//发射源的形状
fireworksEmitter.emitterShape = kCAEmitterLayerLine;
//渲染模式
fireworksEmitter.renderMode = kCAEmitterLayerAdditive;
//用于初始化随机数产生的种子 (1 -- 10)
//fireworksEmitter.seed = (arc4random()%10)+1;
//自旋转速度
fireworksEmitter.spin = 0.f;
//粒子的缩放比例
fireworksEmitter.scale = 1.f;
//粒子速度
fireworksEmitter.velocity = 1.f;
// Create the rocket
CAEmitterCell *rocket = [CAEmitterCell emitterCell];
rocket.birthRate = 5.0;
//周围发射角度
rocket.emissionRange = M_PI/6; // some variation in angle
rocket.velocity = 300;
//粒子的速度范围
//rocket.velocityRange = 400;
//粒子y方向的加速度分量
rocket.yAcceleration = 275;
rocket.lifetime = 1.02; // we cannot set the birthrate < 1.0 for the burst
NSString *imageName = [NSString stringWithFormat:@"liveRoom_zan_da_%zd", getRandomNumber(1, 12)];
rocket.contents = (id) [[UIImage imageNamed:imageName] CGImage];
rocket.scale = 0.3;
//rocket.color = [[UIColor redColor] CGColor];
//rocket.greenRange = 1.0; // different colors
//rocket.redRange = 1.0;
//rocket.blueRange = 1.0;
rocket.spinRange = M_PI/2; // slow spin
rocket.alphaRange = 1.0;
// the burst object cannot be seen, but will spawn the sparks
// we change the color here, since the sparks inherit its value
CAEmitterCell *burst = [CAEmitterCell emitterCell];
burst.birthRate = 1.0; // at the end of travel
burst.velocity = 0;
//burst.scale = 2.5;
//粒子 red 在生命周期内的改变速度
//burst.redSpeed = -1.5;
//burst.blueSpeed = +1.5;
//burst.greenSpeed = +1.0;
burst.lifetime = 1.35;
burst.alphaSpeed = -2.3;
// putting it together
//fireworksEmitter.emitterCells = [NSArray arrayWithObject:rocket];
//rocket.emitterCells = [NSArray arrayWithObject:burst];
return fireworksEmitter;
}
#pragma mark - UIView的,翻转、旋转、偏移、翻页、缩放、取反的动画效果 -
//翻转
- (void)animationFlipWithView:(UIView *)animationView
{
[UIView beginAnimations:@"doflip" context:nil];
[UIView setAnimationDuration:1];
//设置动画淡入淡出
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDelegate:self];
//设置翻转方向
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:animationView cache:YES];
[UIView commitAnimations];
}
//旋转
- (void)animationRotateWithView:(UIView *)animationView
{
//创建一个CGAffineTransform transform对象
CGAffineTransform transform;
//设置旋转度数
transform = CGAffineTransformRotate(animationView.transform, M_PI/6.0);
[UIView beginAnimations:@"rotate" context:nil ];
[UIView setAnimationDuration:2];
[UIView setAnimationDelegate:self];
//获取transform的值
[animationView setTransform:transform];
[UIView commitAnimations];
}
//偏移
-(void)animationMoveWithView:(UIView *)animationView
{
[UIView beginAnimations:@"move" context:nil];
[UIView setAnimationDuration:2];
[UIView setAnimationDelegate:self];
//改变它的frame的x,y的值
animationView.frame = CGRectMake(100, 100, 120, 100);
[UIView commitAnimations];
}
//翻页
-(void)animationCurlUpWithView:(UIView *)animationView
{
[UIView beginAnimations:@"curlUp" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];//指定动画曲线类型,该枚举是默认的,线性的是匀速的
[UIView setAnimationDuration:1];
[UIView setAnimationDelegate:self];
//设置翻页的方向
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:animationView cache:YES];
[UIView commitAnimations];
}
//缩放
-(void)animationScaleWithView:(UIView *)animationView
{
CGAffineTransform transform;
transform = CGAffineTransformScale(animationView.transform, 1.2, 1.2);
[UIView beginAnimations:@"scale" context:nil];
[UIView setAnimationDuration:2];
[UIView setAnimationDelegate:self];
[animationView setTransform:transform];
[UIView commitAnimations];
}
//取反的动画效果是根据当前的动画取他的相反的动画
-(void)animationInvertWithView:(UIView *)animationView
{
CGAffineTransform transform;
transform=CGAffineTransformInvert(animationView.transform);
[UIView beginAnimations:@"Invert" context:nil];
[UIView setAnimationDuration:2];
[UIView setAnimationDelegate:self];
//获取改变后的view的transform
[animationView setTransform:transform];
[UIView commitAnimations];
}
@end