美文网首页
CATransform3D基本动画

CATransform3D基本动画

作者: Timemit | 来源:发表于2017-12-07 23:29 被阅读146次

    最近有看到一些在iOS上面实现的一些动画,通过一些简单的平移,旋转,缩放等等实现了一些特别炫的效果,于是就深入研究了一下,简单整理了一些基本的东西,在这块做一下分享。

    前言

    在学习它之前,我们先来了解一些基本的概念
    1、三维坐标系:视角垂直与屏幕而言,x轴向右,y轴向下,z轴垂直屏幕向外。
    2、坐标系原点:ios默认以图层的左上角点为坐标原点,osx默认以图层左下角为坐标原点。注意是默认,因为图层的坐标原点是可以设置的,下面会介绍。
    3、view于layer的关系:对于UIView对象,layer属性承担了显示的职能,对view设置frame和bounds就是对view的layer设置,因此下面将不区分view和layer。
    4、图层的锚点anchorPoint:是一个CGPoint值,x,y取值范围(0~1),默认为(0.5,0.5) 对于图层本身而言,顾名思义,锚点就用来定位图层的点。锚点有两个职能:(1)与position一同确定图层相对于父图层的位置;(2)作为图层旋转、平移、缩放的中心。
    5、决定图层位置的position:图层的锚点相对于父图层坐标系原点的偏移。

    CATransform3D

    1.CATransform3D概念

    CATransform3D(三维变换矩阵) 的数据结构定义了一个同质的三维变换(4x4 CGFloat值的矩阵),用于图层的旋转,缩放,偏移,歪斜和应用的透视。CATransform3D的结构体定义及各成员变量的职能如下:

    struct CATransform3D
    {
    CGFloat  m11(x缩放), m12(y切变), m13(旋转), m14();
    CGFloat  m21(x切变), m22(y缩放), m23(),    m24();
    CGFloat  m31(旋转),  m32( ),    m33(),    m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。正直/负值都有意义);
    CGFloat  m41(x平移), m42(y平移), m43(z平移),m44();
    };
    

    里面各个参数的设置可以进行相应的变换。

    2.Translation(平移变换)

    平移变换有两个方法:

    (1)CATransform3D CATransform3DTranslate (CATransform3D t, CGFloat tx,
    CGFloat ty, CGFloat tz)
    (2)CATransform3D CATransform3DMakeTranslation (CGFloat tx,
    CGFloat ty, CGFloat tz)

    两个的区别:第一个可以在t的基础上再叠加变换,而第二个每次变换都是以初始状态为基础。

    官方文档:Returns a transform that translates by ‘(tx, ty, tz)’. t’ = [1 0 0 0; 0 1 0 0; 0 0 1 0; tx ty tz 1].即返回一个4x4的矩阵,将该矩阵立起来后看:

    1    0    0    0  
    0    1    0    0  
    0    0    1    0   
    tx   ty   tz   1
    

    对应CATransform3D的公式,tx、ty、tz参数分别用于x平移、y平移、z平移。x、y的平移比较好理解,对于tz来说,值越大,那么图层就越往外(接近屏幕),值越小,图层越往里(屏幕里)。
    tx:X轴偏移位置,往下为正数。
    ty:Y轴偏移位置,往右为正数。
    tz:Z轴偏移位置,往外为正数。

    //平移
    -(void)transition{
        CATransform3D t = CATransform3DIdentity;
        //x方向平移50 y方向平移50
        [UIView animateWithDuration:2.0 animations:^{
            self.backImg.layer.transform = CATransform3DTranslate(t, 50, 50, 0);
        } completion:^(BOOL finished) {    
             //动画执行完成还原到初始状态
            self.backImg.layer.transform = CATransform3DIdentity;
        }];
    }
    

    3.Rotation(旋转变换)

    (1)CATransform3D CATransform3DRotate (CATransform3D t, CGFloat angle,
    CGFloat x, CGFloat y, CGFloat z)
    (2)CATransform3D CATransform3DMakeRotation (CGFloat angle, CGFloat x,
    CGFloat y, CGFloat z)

    angle:旋转的弧度,所以要把角度转换成弧度:角度 * M_PI / 180
    x:绕X轴进行旋转。值范围-1 — 1之间
    y:绕Y轴进行旋转。值范围-1 — 1之间
    z:绕Z轴进行旋转。值范围-1 — 1之间
    旋转方向:旋转遵循�左手定则。以绕y轴旋转为例,当参数y为正时,左手大拇指指向y轴正向,手掌弯曲方向即为旋转方向,此时从大拇指指向往里看是顺时针方向。若y参数为负,将大拇指指向y轴负向,此时从大拇指指向往里看是逆时针方向。 绕x轴z轴旋转判断方法相同。

    //旋转
    -(void)rotate{
        CATransform3D t = CATransform3DIdentity;
        //x,y,z 的值决定旋转轴的方向
        [UIView animateWithDuration:2.0 animations:^{
            self.backImg.layer.transform = CATransform3DRotate(t, 60 * (M_PI / 180), 1, 1, 0);
        } completion:^(BOOL finished) {
            self.backImg.layer.transform = CATransform3DIdentity;
        }];
    }
    
    原始状态.png
    x轴旋转.png
    y轴旋转.png
    z轴旋转.png
    x和y轴旋转.png

    4.Scale(缩放变换)

    (1)CATransform3D CATransform3DScale (CATransform3D t, CGFloat sx,
    CGFloat sy, CGFloat sz)
    (2)CATransform3D CATransform3DMakeScale (CGFloat sx, CGFloat sy,
    CGFloat sz)

    官方文档L:Returns a transform that scales by `(sx, sy, sz)’: * t’ = [sx 0 0 0; 0 sy 0 0; 0 0 sz 0; 0 0 0 1]. 返回的矩阵为:

    sx   0    0    0  
    0    sy   0    0  
    0    0    sz   0  
    0    0    0    1
    

    sx、sy、sz参数对应x、y、z轴的比例缩放,>0为正向比例缩放,<0为反向比例缩放,参数等于1时表示不进行缩放。
    sz:整体比例变换时,也就是m11(sx)== m22(sy)时,若m33(sz)>1,图形整体缩小,若0<1,图形整体放大,若m33(sz)<0,发生关于原点的对称等比变换。
    以x,y轴为例:

    //缩放
    -(void)scale{
        CATransform3D t = CATransform3DIdentity;
        //x,y缩放
        [UIView animateWithDuration:2.0 animations:^{
            self.backImg.layer.transform = CATransform3DScale(t,0.5, 0.5, 1);
        } completion:^(BOOL finished) {
            self.backImg.layer.transform = CATransform3DIdentity;
        }];
    }
    
    缩小0.5.png
    放大1.5.png

    综合案例

    通过上述三种动画方式我们做一个可以旋转的立方体。
    具体思路:我们需要通过CATransformLayer创建一个容器,然后通过CALayer创建六个页面,通过平移旋转的方式将它们组合到一起放到容器里,通过给立方体添加手势实现旋转,具体的效果如下:


    效果.gif

    具体代码如下:

    -(void)diceCube{
        CATransformLayer * cube = [CATransformLayer layer];
        //第一个面
        //+z方向平移
        CATransform3D t = CATransform3DMakeTranslation(0, 0, 50);
        [cube addSublayer:[self diceFaceWithTransform:t withIndex:1]];
        
        //第二个面
        //+x方向平移
        t = CATransform3DMakeTranslation(50, 0, 0);
        //y轴旋转90度
        t = CATransform3DRotate(t, 90 * (M_PI / 180), 0, 1, 0);
        [cube addSublayer:[self diceFaceWithTransform:t withIndex:2]];
        
        //第三面
        //+y方向平移
        t = CATransform3DMakeTranslation(0, 50, 0);//CATransform3D CATransform3DInvert (CATransform3D t);
        //x轴旋转90度
        t = CATransform3DRotate(t, 90 * (M_PI / 180), -1, 0, 0);
        [cube addSublayer:[self diceFaceWithTransform:t withIndex:3]];
        
        //第四面
        //-x方向平移
        t = CATransform3DMakeTranslation(-50, 0, 0);
        //y轴旋转90度
        t = CATransform3DRotate(t, 90 * (M_PI / 180), 0, -1, 0);
        [cube addSublayer:[self diceFaceWithTransform:t withIndex:4]];
        
        //第五面
        //-y方向平移
        t = CATransform3DMakeTranslation(0, -50, 0);
        //x轴旋转90度
        t = CATransform3DRotate(t, 90 * (M_PI / 180), 1, 0, 0);
        [cube addSublayer:[self diceFaceWithTransform:t withIndex:5]];
        
        //第六面
        //-z方向平移
        t = CATransform3DMakeTranslation(0, 0, -50);
        t = CATransform3DRotate(t, 180 * (M_PI / 180), 1, 0, 0);
        [cube addSublayer:[self diceFaceWithTransform:t withIndex:6]];
        //旋转30度
    //    cube.transform = CATransform3DMakeRotation(30 * (M_PI / 180), 1, 1, 1);
        
        //设置中心点的位置
        cube.position = CGPointMake(200, 200);
        [self.view.layer addSublayer:cube];
        self.cube = cube;
    //    CATransform3D transA = CATransform3DMakeScale(1.0, 1.0, 1.0);
    //    transA = CATransform3DRotate(transA, 180 * (M_PI / 180), 1, 1, 1);
    //    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
    //    animation.duration          = 2;
    //    animation.autoreverses      = YES;
    //    animation.repeatCount       = 100;
    //    animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.5, 0.5, 0.5)];
    //    animation.toValue           = [NSValue valueWithCATransform3D:transA];
    //    [cube addAnimation:animation forKey:nil];
    }
    -(CALayer *)diceFaceWithTransform:(CATransform3D)transform withIndex:(NSInteger)index{
        CATextLayer *face = [CATextLayer layer];
        face.bounds = CGRectMake(0, 0, 100, 100);
        face.string = [NSString stringWithFormat:@"第%zd面",index];
        face.fontSize = 20;
        face.contentsScale = 2;
        face.alignmentMode = @"center";
        face.truncationMode = @"middle";
        face.transform = transform;
        CGFloat red = (rand() / (double)INT_MAX);
        CGFloat green = (rand() / (double)INT_MAX);
        CGFloat blue = (rand() / (double)INT_MAX);
        face.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
        return face;
    }
    

    注:这块有个细节就是每个面都有字,如果没有考虑旋转方向的时候有的面的字是反的,所以添加每一个面的时候要考虑这个面的旋转方向(字始终在你创建该面的上方)

    添加拖动手势的代码

    - (void)pan:(UIPanGestureRecognizer *)recognizer{
        CGFloat w = [UIScreen mainScreen].bounds.size.width;
        CGFloat h = [UIScreen mainScreen].bounds.size.height;
        //获取到的是手指移动后,在相对坐标中的偏移量(以手指接触屏幕的第一个点为坐标原点)
        CGPoint translation = [recognizer translationInView:self.view];
        NSLog(@"x = %f ------ y = %f",translation.x, translation.y);
        CATransform3D transform = CATransform3DIdentity;
        transform = CATransform3DRotate(transform, translation.x * (M_PI * 2 / w), 0, 1, 0);
        transform = CATransform3DRotate(transform, translation.y * (-M_PI * 2 / h), 1, 0, 0);
        self.cube.transform = transform;
    }
    

    总结

    总的来说,要做出比较炫的动效,需要熟悉其中动画的基本知识和运作原理。每一个小的细节都会导致效果的不同,我也是一个新手,大学里学的线性代数对于矩阵的一些简概念和简单操作,现在全忘完了,看到变换里有矩阵相关的一些东西,一脸懵逼,最后查了一些资料才勉强看懂,看来以后得多看看以前学过的数学知识。
    非常感谢大家!希望大家共同进步!多提宝贵意见!
    最后附上项目代码
    CATransform3DDemo

    相关文章

      网友评论

          本文标题:CATransform3D基本动画

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