Core Animation

843 阅读7分钟

简介

  • 它是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍。也就是说,使用少量的代码就可以实现非常强大的功能
  • Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程。
  • 要注意的是,Core Animation是直接作用在CALayer上的,并非UIView
  • Core Animation将大部分实际的绘图任务交给了图形硬件来处理,图形硬件会加速图形渲染的速度。这种自动化的图形加速技术让动画拥有更高的帧率并且显示效果更加平滑,不会加重CPU的负担而影响程序的运行速度

Core Animation类的继承关系图

image.png

黑线代表继承,黑色文字代表类名,白色文字代表属性。其中CAMediaTiming是一个协议(protocol)

Core Animation应用

  • 常用属性

    duration : 动画的持续时间
    beginTime : 动画的开始时间
    repeatCount : 动画的重复次数
    autoreverses : 执行的动画按照原动画返回执行

  • animationwithkeypath

    transform.scale = 比例缩放
    transform.scale.x = 宽的比例缩放
    transform.scale.y = 高的比例缩放
    transform.rotation.z = 以Z轴为中心旋转
    opacity = 透明度
    margin = 布局
    zPosition = 翻转
    backgroundColor = 背景颜色
    cornerRadius = 圆角
    borderWidth = 边框宽
    bounds = 大小
    contents = 内容
    contentsRect = 内容大小
    cornerRadius = 圆角
    frame = 大小位置
    hidden = 显示隐藏
    mask
    masksToBounds
    position = 位置
    shadowColor = 阴影颜色
    shadowOffset = 阴影偏移
    shadowOpacity = 阴影透明度
    shadowRadius = 阴影圆角

  • timingFunction : 控制动画的显示节奏系统提供五种值选择,分别是:

    kCAMediaTimingFunctionLinear 线性动画 kCAMediaTimingFunctionEaseIn 先慢后快(慢进快出) kCAMediaTimingFunctionEaseOut 先快后慢(快进慢出) kCAMediaTimingFunctionEaseInEaseOut 先慢后快再慢 kCAMediaTimingFunctionDefault 默认,也属于中间比较快

  • delegate : 动画代理。检测动画的执行和结束。

- (void)animationDidStart:(CAAnimation *)anim;//开始动画
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;//动画结束
  • path:关键帧动画中的执行路径

  • type : 过渡动画的动画类型,系统提供了四种过渡动画。

    kCATransitionFade 渐变效果 kCATransitionMoveIn 进入覆盖效果 kCATransitionPush 推出效果 kCATransitionReveal 揭露离开效果

  • subtype : 过渡动画的动画方向

    kCATransitionFromRight 从右侧进入 kCATransitionFromLeft 从左侧进入 kCATransitionFromTop 从顶部进入 kCATransitionFromBottom 从底部进入

transform 形变

平移、缩放、旋转,一般配合animateWithDuration做动画效果

  • CGAffineTransform是一个结构体,类似我们常见的CGRect、size,创建是利用CG....Make(),CGAffineTransform同样也是如此。

  • CGAffineTransformMake(a,b,c,d,tx,ty) ad缩放bc旋转tx,ty位移,基础的2D矩阵 公式 x=ax+cy+tx y=bx+dy+ty 1.矩阵的基本知识:structCGAffineTransform{ CGFloata,b,c,d; CGFloattx,ty;};CGAffineTransformCGAffineTransformMake

  • 补充 两个矩阵的乘法仅当第一个矩阵A的列数和另一个矩阵B的行数相等时才能定义。如Am×n矩阵和Bn×p矩阵,它们的乘积C是一个m×p矩阵

 ,它的一个元素:

并将此乘积记为:

 [8]  .

例如:

矩阵的乘法满足以下运算律:

结合律:

左分配律:

右分配律:

矩阵乘法不满足交换律

  • 平移Translation
//这句话代表:tempView 水平方向平移100,垂直方向平移50.
  self.tempView.transform = CGAffineTransformMakeTranslation(100, 50);
  • 缩放Scale
//宽度缩放0.5倍,高度缩放0.3倍
   self.tempView.transform = CGAffineTransformMakeScale(0.5, 0.3);
  • 旋转Rotation
//选转是旋转多少角度。iOS中 180°`M_PI`,90°`M_PI_2`,45°`M_PI_4`
//正数是顺时针旋转,负数是逆时针方向旋转
//顺时针旋转180度
   self.tempView.transform = CGAffineTransformMakeRotation(M_PI_2);
  • 多种形变嵌套使用
[UIView animateWithDuration:2.f animations:^{
       //1.首先创建一个CGAffineTransform平移对象
       CGAffineTransform  translation = CGAffineTransformMakeTranslation(100, 50);
       //2.将平移对象嵌套到缩放形变中
       CGAffineTransform  scale = CGAffineTransformScale(translation, 0.5, 0.3);
       
       //3.将上边的形变再嵌套
       self.tempView.transform = CGAffineTransformRotate(scale, M_PI_2);
 }];
  • 累加
[UIView animateWithDuration:1.0 animations:^{
       
       static double angle = 0;
       angle += M_PI_4;
       self.tempView.transform = CGAffineTransformMakeRotation(angle);
   }];
[UIView animateWithDuration:1.0 animations:^{
   
       self.tempView.transform = CGAffineTransformRotate(self.tempView.transform, M_PI_4));
 }];
  • 清除
self.tempView.transform = CGAffineTransformMakeRotation(0);
self.tempView.transform = CGAffineTransformIdentity;
  • 图层的平移、旋转和缩放到底是如何实现的
在二维坐标中,我们将矩阵的坐标点设置为:
A :
[x y 1],
仿射变换的基础变换矩阵为:
B:
[a b 0]
 c d 0
[tx ty 1]

通过A * B来得到一个变换之后的矩阵:
C = [ (a*x+c*y+tx)   (b*x+d*y+ty)  (1) ]

在这里,我们假设C = [x' y' 1];

x' = a*x + c*y + tx
y' = b*x + d*y + ty

平移
x' = a*x + c*y + tx
y' = b*x + d*y + ty
通过这两个等式,我们可以发现,abcd在等于01的时候,会对结果又很大的影响。例如:
a = 1b = 0c = 0d = 1
上面的等式为变成:
x' = x + tx
y' = y + ty

这样的点C(x',y') 就相当于点A(x,y) 在原来的基础上平移了一段距离

CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)就是通过这样的计算实现的。

缩放
x' = a*x + c*y + tx
y' = b*x + d*y + ty
将c,b,tx,ty 均置为0,上面的等式变为:
x' = a*x
y' = d*y

a,d分别对应CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)方法中的参数sx和sy

旋转
x' = a*x + c*y + tx
y' = b*x + d*y + ty

a = cosa , b = sina , c = -sina , d = cosa , tx = 0 , ty = 0,上面的等式变为:
x' = cosa*x - sina * y
y' = sina*x + cosa * y
这样得到的x’和y’就是x和y旋转角度a之后得到的值。
将a = cosa , b = sina , c = -sina , d = cosa , tx = 0 , ty = 0代入到我们的基础变换矩阵中就得到仿射旋转矩阵:

角度a对应方法CGAffineTransformMakeRotation(CGFloat angle)中的angle参数。

以上变换都可以使用基础变换函数:CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty)带入a,b,c,d,tx,ty的值得到变换结果。

混合变换

使用CGAffineTransformMake系列方法在做变换的时候,每次做变换的时候都会清楚之前变换的效果,也就是每次的变换都是以图层的初始位置为参照点进行的,但是如果我们希望视图每次的变换都是在上一次变换的基础上进行的话,那么怎么办呢?
Core Graphics框架还为我们提供了一系列的函数可以在一个变换的基础上做更深层次的变换

CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)

分别使用CGAffineTransformMakeCGAffineTransform对图层做变换(对视图的transform和图层的affineTransform属性操作都能看到同样的效果)

- (IBAction)button1Click:(UIButton *)sender {
    if (sender.isSelected) {
        sender.selected = NO;
        //通过平移,回到当前视图相对于frame的(0,0)位置
        self.testView.transform = CGAffineTransformMakeTranslation(0,0);
    }else {
        sender.selected = YES;
        //通过平移,平移到当前视图相对于frame的(0,50)位置
            self.testView.transform = CGAffineTransformMakeTranslation(0,50);
    }
}
- (IBAction)button2Click:(UIButton *)sender {
    if (sender.isSelected) {
        sender.selected = NO;
        self.testView.layer.affineTransform = CGAffineTransformTranslate(CGAffineTransformIdentity, 0, 0);
        // self.testView.transform / CGAffineTransformIdentity
    }else {
        sender.selected = YES;
            self.testView.layer.affineTransform = CGAffineTransformTranslate(CGAffineTransformIdentity, 0, 50);
       // self.testView.transform / CGAffineTransformIdentity
    }
}

3D变换

在CALyer中,同样存在一个transform属性,不过它是CATransform3D类型,用来做3D变换。之前,我们提到过图层的zPosition属性,transform属性就用用来操作zPosition属性来控制图层靠近或者远离用户的视角,从而达到3D变换的效果。
CATransform3D也是一个矩阵,但是和2x3的矩阵不同,CATransform3D是一个可以在3维空间内做变换的4x4的矩阵

CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz) 
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)

元素m34就是用来控制透视投影效果的,元素m34用于按比例缩放X和Y的值来计算到底要离视角多远。我们可以通过设置m34为-1.0 / d来应用透视效果,d代表了想象中视角和屏幕之间的距离,单位为像素,通常情况下,d的值在500-1000之间,看起来会比较舒服

UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"image_1.jpg"]];
    imageView.frame = CGRectMake(75, 100, 250, 250);
    [self.view addSubview:imageView];
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = - 1.0 / 500.0;
    transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
    imageView.layer.transform = transform;

github.com/Zws-China/C…

Core Graphics

UIBezierPath