1、Core Animaltion
image-20221113200244403.png
什么是Animation(动画),简单点说就是在一段时间内,显示的内容发生了变化.对CALayer来说就是在一段时间内,其Animatable Property发生了变化.从CALayer(CA = Core Animation)类名来看就可以看出iOS的Layer就是为动画而生的,便于实现良好的交互体验. 这里涉及到两个东西: 一是Layer(基类CALayer),二是Animation(基于CAAnimation). Animation作用于Layer.CALayer提供了接口用于给自己添Animation. 用于显示的Layer本质上讲是一个Model,包含了Layer的各种属性值. Animation则包含了动画的时间,变化,以及变化的速度
Core Animation的动画执⾏过程都是在后台操作的,不会阻塞主线程。
动画的两个重要的点:
一是中间状态的插值计算(Interpolation),
二是动画节奏控制(Timing); 有时候插值计算也和Timing有一定关系. 如果状态是一维空间的值(比如透明度),那么插值计算的结果必然再起点值和终点值之间,如果状态是二维空间的值(比如position),那么一般情况下插值得到的点会落在起点和终点之间的线段上(当然也有可能连线是圆滑曲线)
1、CAAnimation 简介:
CAAimation是所有动画对象的基类,负责控制动画的持续时间和速度,是个抽象类,不能直接使⽤用,应该使⽤用它具体的⼦子类 属性说明:
-
removedOnCompletion// 动画是否⾃自动移出 -
duration// 动画持续时间 -
speed//速度 -
timeOffset// 动画时间的偏移 -
repeatCount// 动画重复执⾏行的次数(HUGE_VALF无限次) -
repeatDuration// 动画重复执⾏行的总时间 -
autoreverses// 反转动画 -
delegate// 代理 -
fillMode// 填充模式 -
timingFunction//速度控制函数
fillMode
作用就是决定当前对象过了非active时间段的行为. 比如动画开始之前,动画结束之后。如果是一个动画CAAnimation,则需要将其removedOnCompletion设置为NO,要不然fillMode不起作用.
1.
kCAFillModeRemoved这个是默认值,也就是说当动画开始前和动画结束后,动画对 layer都没有影响,动画结束后,layer会恢复到之前的状态2.
kCAFillModeForwards当动画结束后,layer会一直保持着动画最后的状态3.
kCAFillModeBackwards这个和kCAFillModeForwards是相对的,就是在动画开始前,你只要将动画加入了⼀个layer,layer便立即进入动画的初始状态并等待动画开始.你可以这样设定测试代码,将一个动画加⼊一个layer的时候延迟5秒执⾏行.然后就会发现在动画没有开 始的时候,只要动画被加入了layer,layer便处于动画初始状态4.
kCAFillModeBoth理解了上⾯两个,这个就很好理解了,这个其实就是上⾯两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态.
Timing Function
被用于变化起点和终点之间的插值计算.形象点说是Timing Function决定了动画运行的节奏(Pacing),比如是均匀变化(相同时间变化量相同),先快后慢,先慢后快还是先慢再快再慢.
1.
kCAMediaTimingFunctionLinear:线性(匀速)
2.kCAMediaTimingFunctionEaseIn:先慢
3.kCAMediaTimingFunctionEaseOut:后慢
4.kCAMediaTimingFunctionEaseInEaseOut:先慢 后慢 中间快
每个CALayer和CAAnimation实例都有自己本地时间的概念,是根据beginTime、timeOffset和speed属性计算,类似于相对坐标转换,
//暂停动画
- (void)pauseAnimation {
// CACurrentMediaTime(): 当前媒体时间,表示系统启动后到当前的秒数,当系统重启后这个时间也重置
CFTimeInterval pauseTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
// 设置动画的时间的偏移
layer.timeOffset = pauseTime;
layer.speed = 0;
}
//恢复动画
- (void)resumeAnimation {
// 获取暂停时的时间
CFTimeInterval pasuseTime = [layer timeOffset];
layer.speed = 1;
layer.timeOffset = 0;
layer.beginTime = 0;
// 设置开始的时间(继续动画,这样设置相当于让动画等待的秒数等于暂停的秒)
layer.beginTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pasuseTime;
}
2、CABasicAnimation
用于实现layer属性值从一个值(fromValue)到另外一个值(toValue)变化的简单动画,比如旋转、缩放、逐渐透明、移动等。
1.fromValue:keyPath相应属性的初始值
2.byValue:keyPath相应属性的中间值
3.toValue:keyPath相应属性的结束值
如果
fillMode=kCAFillModeForwards``removedOnComletion=NO,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变。比如,CALayer的position初始值为(0,0),CABasicAnimation的fromValue为(10,10),toValue为(100,100),虽然动画执行完毕后图层保持在(100,100)这个位置,实质上图层的position还是为(0,0)
- (CAAnimation *)scaleAnimation {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
// 1、初始值
animation.fromValue = @1.0;
// 2、目标值
animation.toValue = @2.0;
// 3、变化的值, fromValue ~ toValue 值的变化量
// animation.byValue = @1.0;
// 4、动画时间
animation.duration = 2.0;
/* 5、动画的填充模式:
kCAFillModeForwards
kCAFillModeBackwards
kCAFillModeBoth
kCAFillModeRemoved
*/
animation.fillMode = kCAFillModeForwards;
// 6、动画后是否移除动画后的状态(回到原始状态),默认是YES, 前提是要设置fillModle为:kCAFillModeForwards
animation.removedOnCompletion = NO;
// 7、是否有回复效果
animation.autoreverses = YES;
// 8、设置动画重复次数
animation.repeatCount = HUGE_VALF; // HUGE_VALF 最大浮点数,表示无限次重复
// 9、播放动画的速度
animation.speed = 2;
//备注设置了动画的起点和终点之后,中间的值都是通过插值方式计算出来的.插值计算的结果由timingFunction指定,默认timingFunction为nil,会使用liner的,也就是变化是均匀的.
return animation;
}
- (CAAnimation *)rotationAnimation {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
// animation.fromValue = @0;
// animation.toValue = @(2 * M_PI);
//顺时针360度旋转
animation.byValue = @(2 * M_PI);
//访问马赫时间。它返回了设备自上次启动后的秒数,用来做时间延迟
animation.beginTime = CACurrentMediaTime() + 2;
animation.duration = 2.0;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.autoreverses = YES;
animation.repeatCount = HUGE_VALF; // HUGE_VALF 最大浮点数,表示无限次重复
return animation;
}
- (CAAnimation *)boundsAnimation {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
animation.fromValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 100, 100)];
animation.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 300, 300)];
animation.duration = 2.0;
return animation;
}
- (CAAnimation *)positionAnimation {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
//设置初始值
animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(150, 50)];
//设置结束值
animation.toValue = [NSValue valueWithCGPoint:CGPointMake(150, 550)];
//动画时间
animation.duration = 2.0;
//填充模式
animation.fillMode = kCAFillModeForwards;
//动画结束后是否自动移除
animation.removedOnCompletion = NO;
//反转动画
animation.autoreverses = YES;
//循环次数
animation.repeatCount = HUGE_VALF; // HUGE_VALF 最大浮点数,表示无限次重复
/* 动画的线性变换(动画速度变化)
kCAMediaTimingFunctionLinear 匀速
kCAMediaTimingFunctionEaseIn 加速
kCAMediaTimingFunctionEaseOut 减速
kCAMediaTimingFunctionEaseInEaseOut 缓慢进入缓慢出去
kCAMediaTimingFunctionDefault 默认
*/
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
return animation;
}
3、CATransition
CATransition 用于视图的页面切换、页面更新时的转场动画,提供了多种type和subType转场效果 能够为层提供移出屏幕和移入屏幕的动画效果
type: 转场动画效果(API中公开的效果只有四种,其他为私有)。 kCATransitionFade 渐变 kCATransitionMoveIn 覆盖 kCATransitionPush 推出 kCATransitionReveal 揭开
subtype:动画过渡方向
startProgress:动画起点(在整体动画的百分比)
endProgress:动画终点(在整体动画的百分比)转场效果:
1. fade 淡出效果
2. moveIn 进入效果
3. push 推出效果
4. reveal 移出效果未公开的:
5. cube 立方体翻转效果
6. suckEffect 抽走效果
7. rippleEffect 水波效果
8. pageCurl 翻开页效果
9. pageUnCurl 关闭页效果
10. cameraIrisHollowOpen 相机镜头打开效果
11. cameraIrisHollowClose 相机镜头关闭效果
CATransition *transitionAni = [CATransition animation];
transitionAni.duration = 1.0;
transitionAni.type = kCATransitionPush;
// transitionAni.type = @"push";
// 转场的方向:`fromLeft', `fromRight', `fromTop' `fromBottom'
transitionAni.subtype = @"fromRight";
// 开始转场和结束转场的进度位置
// transitionAni.startProgress = 0.5;
// transitionAni.endProgress = 1;
[imageView.layer addAnimation:transitionAni forKey:@"keyAnimation"];
4、CAKeyframeAnimation
// 1、通过values设置关键帧
- (CAAnimation *)keyframeAnimation {
//创建核心动画
CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
keyAnimation.duration = 4;
keyAnimation.autoreverses = YES;
keyAnimation.repeatCount = HUGE_VALF;
keyAnimation.fillMode = kCAFillModeForwards;
keyAnimation.removedOnCompletion = NO;
//要执行的动画
NSValue *point_1 = [NSValue valueWithCGPoint:CGPointMake(Width / 2,0)];
NSValue *point_2 = [NSValue valueWithCGPoint:CGPointMake(50,Height / 2)];
NSValue *point_3 = [NSValue valueWithCGPoint:CGPointMake(Width / 2,Height - 50)];
NSValue *point_4 = [NSValue valueWithCGPoint:CGPointMake(Width - 50,Height / 2)];
NSValue *point_5 = [NSValue valueWithCGPoint:CGPointMake(Width / 2,0)];
// values:设置关键帧(多个目标点)
keyAnimation.values = @[point_1,point_2,point_3,point_4,point_5];
// 设置每一帧所在的时间比例
keyAnimation.keyTimes = @[@0, @0.2, @0.5, @0.6,@1.0];
/* 插值计算模式:
kCAAnimationLinear 关键帧之间进行插值计算(线性的)
kCAAnimationDiscrete 关键帧之间不进行插值计算(离散的)
kCAAnimationPaced 关键帧之间匀速切换,keyTimes\timingFunctions的设置将不起作用
kCAAnimationCubic 关键帧进行圆滑曲线相连后插值计算
kCAAnimationCubicPaced 匀速并且关键帧进行圆滑曲线相连后插值计算
*/
keyAnimation.calculationMode = kCAAnimationLinear;
return keyAnimation;
}
//通过path设置动画
// 1、绘制路径
- (UIBezierPath *)path {
// 椭圆
//UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:self.view.bounds];
// 圆角矩形
//UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.view.bounds cornerRadius:50];
// 内切圆
//UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(40, 100, 300, 300)];
// 贝塞尔曲线
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, Height)];
CGPoint point_1 = CGPointMake(Width, Height);
CGPoint controPoint_1 = CGPointMake(Width / 2, - Height);
CGPoint controPoint_2 = CGPointMake(Width / 4 * 3, Height);
//[path addQuadCurveToPoint:point_1 controlPoint:controPoint_1];
[path addCurveToPoint:point_1 controlPoint1:controPoint_1 controlPoint2:controPoint_2];
return path;
}
//2、设置动画
- (CAAnimation *)keyframeAnimation {
CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
keyAnimation.duration = 4;
keyAnimation.autoreverses = YES;
keyAnimation.repeatCount = HUGE_VALF;
keyAnimation.fillMode = kCAFillModeForwards;
keyAnimation.removedOnCompletion = NO;
keyAnimation.calculationMode = kCAAnimationLinear;
keyAnimation.path = [self path].CGPath;
return keyAnimation;
}
备注 calculationMode提供如下几种模式:
kCAAnimationLinear 关键帧之间进行插值计算(线性的)
kCAAnimationDiscrete 关键帧之间不进行插值计算(离散的),
kCAAnimationPaced 关键帧之间匀速切换,
keyTimes\timingFunctions的设置将不起作用
kCAAnimationCubic 关键帧进行圆滑曲线相连后插值计算
kCAAnimationCubicPaced 匀速并且关键帧进行圆滑曲线相连后插值计算,此时keyTimes以及timingFunctions也是无效的
5、CASpringAnimation
用于实现弹簧动画
CASpringAnimation`的重要属性
mass:质量(影响弹簧的惯性,质量越大,弹簧惯性越大,运动的幅度越大)
stiffness:弹性系数(弹性系数越大,弹簧的运动越快)
damping:阻尼系数(阻尼系数越大,弹簧的停止越快)
initialVelocity:初始速率(弹簧动画的初始速度大小,弹簧运动的初始方向与初始速率的正负一致,若初始速率为0,表示忽略该属性)
settlingDuration:结算时间(根据动画参数估算弹簧开始运动到停止的时间,动画设置的时间最好根据此时间来设置)
- (void)springAni {
CASpringAnimation * ani = [CASpringAnimation animationWithKeyPath:@"bounds"];
ani.mass = 10.0; //质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大
ani.stiffness = 5000; //刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快
ani.damping = 100.0;//阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快
ani.initialVelocity = 5.f;//初始速率,动画视图的初始速度大小;速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反
ani.duration = ani.settlingDuration;
ani.toValue = [NSValue valueWithCGRect:self.centerShow.bounds];
ani.removedOnCompletion = NO;
ani.fillMode = kCAFillModeForwards;
ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[self.cartCenter.layer addAnimation:ani forKey:@"boundsAni"];
}
5、CAAnimationGroup
- (CAAnimation *)positionAnimation {
CAKeyframeAnimation *keyAni = [CAKeyframeAnimation animationWithKeyPath:@"position"];
keyAni.duration = 3.0;
keyAni.fillMode = kCAFillModeForwards;
keyAni.removedOnCompletion = NO;
keyAni.path = [self path].CGPath;
return keyAni;
}
- (CAAnimation *)rotationAnimation {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.beginTime = 3.0;
animation.duration = 2.0;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.byValue = @(2 * M_PI);
return animation;
}
- (CAAnimation *)downAnimation {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.beginTime = 3.0;
animation.duration = 2.0;
animation.byValue = [NSValue valueWithCGPoint:CGPointMake(-100, Height)];
return animation;
}
- (UIBezierPath *)path {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, Height)];
CGPoint toPoint = CGPointMake(Width, 0);
CGPoint controlPoint = CGPointMake(Width,Height);
[path addQuadCurveToPoint:toPoint controlPoint:controlPoint];
return path;
}
- (CAAnimation *)animationGroup {
// 创建一个动画组,用于组合多种动画
CAAnimationGroup * aniGroup = [CAAnimationGroup animation];
// 动画组的完成时间
aniGroup.duration = 5.0;
aniGroup.fillMode = kCAFillModeForwards;
aniGroup.removedOnCompletion = NO;
// 组合多个动画
aniGroup.animations = @[[self positionAnimation],[self rotationAnimation],[self downAnimation]];
return aniGroup;
}
隐式动画
简单来说事务是核心动画里面的一个基本的单元,动画的产生必然伴随着
layer的Animatable属性的变化,而layer属性的变化必须属于某一个事务。 因此,核心动画依赖事务。事务的作用:保证一个或多个
layer的一个或多个属性变化同时进行事务支持嵌套使用:当最外层的事务
commit后动画才会开始。事务分为隐式和显式: 1.隐式:没有明显调用事务的方法,由系统自动生成事务。比如直接设置一个
layer的position属性,则会在当前线程自动生成一个事务,并在下一 个runLoop中自动commit事务。2.显式:明显调用事务的方法(
[CATransaction begin]和[CATransaction commit])。注意:
只有非根层,才有隐式动画,可以通过事务,手动关闭隐式动画
只有非
root layer才有隐式动画,
[CATransaction begin];
[CATransaction setAnimationDuration:2.0];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[CATransaction setDisableActions:YES]; //设置为YES就关闭动画
self.subLayer.bounds = self.centerShow.layer.bounds;
[CATransaction commit];
隐式动画相关原理:https://www.freesion.com/article/368066390/
图层关系图
image-20221113213319489.png
UIView 动画
1、transform 形变
transform我们一般称为形变属性,其本质是通过矩阵变化改变控件的大小、位置、角度等,这里我们通过一个例子来看一下具体的操作,在下面的例子中我们也会看到UIImageView控件的常用操作。
- 旋转
-(void)rotation:(UIButton *)btn{
[self animation:^{
//注意旋转角度必须是弧度,不是角度
CGFloat angle=M_PI_4;//M开头的宏都是和数学(Math)相关的宏定义,M_PI_4表示四分之派,M_2_PI表示2派
//使用CGAffineTransformMakeRotation获得一个旋转角度形变
//但是需要注意tranform的旋转不会自动在原来的角度上进行叠加,所以下面的方法旋转一次以后再点击按钮不会旋转了
//_imageView.transform=CGAffineTransformMakeRotation(angle);
//利用CGAffineTransformRotate在原来的基础上产生一个新的角度(当然也可以定义一个全局变量自己累加)
_imageView.transform=CGAffineTransformRotate(_imageView.transform, angle);
}];
}
- 缩放
-(void)scale:(UIButton *)btn{
//通常我们使用UIView的静态方法实现动画而不是自己写一个方法
[UIView animateWithDuration:0.5 animations:^{
CGFloat scalleOffset=0.9;
//_imageView.transform=CGAffineTransformMakeScale(scalleOffset, scalleOffset);
_imageView.transform= CGAffineTransformScale(_imageView.transform, scalleOffset, scalleOffset);
}];
}
- 位移
-(void)translate:(UIButton *)btn{
[self animation:^{
CGFloat translateY=50;
//_imageView.transform=CGAffineTransformMakeTranslation(0, translateY);
_imageView.transform=CGAffineTransformTranslate(_imageView.transform, 0, translateY);
}];
}
* 动画速度、延时、转场效果、线性变换等属性
delay:延迟的秒数,
options: 动画的线性变换
UIViewAnimationOptionCurveEaseInOut 进入、出去时加速
UIViewAnimationOptionCurveEaseIn 进入时加速
UIViewAnimationOptionCurveEaseOut 出去时加速
UIViewAnimationOptionCurveLinear 匀速
*/
[UIView animateWithDuration:2 delay:2 options:UIViewAnimationOptionCurveLinear animations:^{
aniView.layer.cornerRadius = 50;
aniView.transform = CGAffineTransformScale(aniView.transform, 2, 2);
} completion:^(BOOL finished) {
aniView.transform = CGAffineTransformIdentity;
aniView.layer.cornerRadius = 0;
}];
//4、带有缓存效应的动画,Damping:缓存只(0~1),值越小缓冲越大, Velocity: 动画播放速度
[UIView animateWithDuration:2 delay:0 usingSpringWithDamping:0.3
initialSpringVelocity:1 options:0 animations:^{
aniView.layer.cornerRadius = 50;
aniView.transform = CGAffineTransformScale(aniView.transform, 2, 2);
} completion:^(BOOL finished) {
aniView.transform = CGAffineTransformIdentity;
aniView.layer.cornerRadius = 0;
}];
/*
options:转场样式
UIViewAnimationOptionTransitionNone = 0 << 20, // default
UIViewAnimationOptionTransitionFlipFromLeft = 1 << 20,
UIViewAnimationOptionTransitionFlipFromRight = 2 << 20,
UIViewAnimationOptionTransitionCurlUp = 3 << 20,
UIViewAnimationOptionTransitionCurlDown = 4 << 20,
UIViewAnimationOptionTransitionCrossDissolve = 5 << 20,
UIViewAnimationOptionTransitionFlipFromTop = 6 << 20,
UIViewAnimationOptionTransitionFlipFromBottom
*/
[UIView transitionFromView:self.view toView:aniView duration:1 options:UIViewAnimationOptionTransitionCurlUp completion:^(BOOL finished) {
}];











网友评论