美文网首页
ios动画~随笔

ios动画~随笔

作者: hello_kitty | 来源:发表于2024-01-19 00:31 被阅读0次

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是所有动画对象的基类,负责控制动画的持续时间和速度,是个抽象类,不能直接使⽤用,应该使⽤用它具体的⼦子类 属性说明:

  1. removedOnCompletion // 动画是否⾃自动移出

  2. duration // 动画持续时间

  3. speed //速度

  4. timeOffset // 动画时间的偏移

  5. repeatCount // 动画重复执⾏行的次数(HUGE_VALF无限次)

  6. repeatDuration // 动画重复执⾏行的总时间

  7. autoreverses // 反转动画

  8. delegate // 代理

  9. fillMode // 填充模式

  10. 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;
}

隐式动画

简单来说事务是核心动画里面的一个基本的单元,动画的产生必然伴随着layerAnimatable属性的变化,而layer属性的变化必须属于某一个事务。 因此,核心动画依赖事务。

事务的作用:保证一个或多个layer的一个或多个属性变化同时进行

事务支持嵌套使用:当最外层的事务commit后动画才会开始。

事务分为隐式和显式: 1.隐式:没有明显调用事务的方法,由系统自动生成事务。比如直接设置一个layerposition属性,则会在当前线程自动生成一个事务,并在下一 个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) {
     }];

相关文章

网友评论

      本文标题:ios动画~随笔

      本文链接:https://www.haomeiwen.com/subject/hhbdodtx.html