学习计划(6) - 动画 - 形变动画

形变动画即形状的旋转,位移,缩放和形状的变化。

官网地址:

https://developer.apple.com/documentation/coregraphics/cgaffinetransform-rb5?language=objc

要想达到我们所需要的结果,需要使用到CGAffineTransform
CGAffineTransform很多人认为这是个类。抱歉这不是类。官网说的很清楚。

An affine transformation matrix for use in drawing 2D graphics.
一个用于绘制2D图形的仿射变换矩阵。

是的。一种矩阵.
打开CGAffineTransform.h 文件我们就可以看到的。

struct CGAffineTransform {
  CGFloat a, b, c, d;
  CGFloat tx, ty;
};

在程序内部我们对其进行变化的时候,其实内部代码改的就是这个矩阵的数据。
具体的矩阵原理 传送门:
https://www.zhihu.com/question/21351965

原理看不懂,我们就来看看源码:

CGPointApplyAffineTransform
返回一个已存在点的仿射变换所产生的点。

方法定义:
CG_EXTERN CGPoint CGPointApplyAffineTransform(CGPoint point,
  CGAffineTransform t) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);


CG_INLINE CGPoint
__CGPointApplyAffineTransform(CGPoint point, CGAffineTransform t)
{
  CGPoint p;
  p.x = (CGFloat)((double)t.a * point.x + (double)t.c * point.y + t.tx);
  p.y = (CGFloat)((double)t.b * point.x + (double)t.d * point.y + t.ty);
  return p;
}

可以看到,参数中的坐标点的计算方式
即ax+cy+tx负责坐标x
即bx+dy+ty负责坐标y

CGSizeApplyAffineTransform
通过对现有高度和宽度的转换来返回高度和宽度。

方法定义
CG_EXTERN CGSize CGSizeApplyAffineTransform(CGSize size, CGAffineTransform t)
  CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);


CG_INLINE CGSize
__CGSizeApplyAffineTransform(CGSize size, CGAffineTransform t)
{
  CGSize s;
  s.width = (CGFloat)((double)t.a * size.width + (double)t.c * size.height);
  s.height = (CGFloat)((double)t.b * size.width + (double)t.d * size.height);
  return s;
}

aw+ch可以得到宽度width
bw+dh可以得到高度height
这样想的其实也简单多了

要是还不行,那只能好好学学高中的矩阵入门基础了,大学的线性代数。

CG_INLINE 表示内联,即在编译的时候将函数体替换函数调用,从而不需要将parameter,return address进行push/pop stack的操作,从而加速app的运行,然而,会增加二进制文件的大小。

CGAffineTransform它所定义的类型很多,我们来介绍一下:

Creating an Affine Transformation Matrix
直接创建一个新的变换矩阵

//设置完整的变形矩阵
CGAffineTransformMake
//旋转
CGAffineTransformMakeRotation
//缩放
CGAffineTransformMakeScale
//位移
CGAffineTransformMakeTranslation
CGAffineTransformMake
它表示一个新的仿射变换矩阵

方法定义:
CGAffineTransform CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty);

CGAffineTransformMake 可以设置比较完整的矩阵.通过它就可以直接设置旋转,缩放,位移的操作的矩阵
它的属性设置可以看下图:


学习计划(6) - 动画 - 形变动画_第1张图片
矩阵模型.png
学习计划(6) - 动画 - 形变动画_第2张图片
计算.png
结果.png

可以看到我们主要是设置它的6个值,最后算出它的坐标x,y。其中第三列的0,0,1是固定不动的哦。

以上的方法,在调用的时候每次都是初始值。
以下的方法,在调用的时候都会之前已经修改过的基础上进行变换。

Modifying Affine Transformations
创建一个新的或者在原来基础上修改的变换矩阵。

方法:

CGAffineTransformTranslate
学习计划(6) - 动画 - 形变动画_第3张图片
translate.gif

它的属性设置图:


学习计划(6) - 动画 - 形变动画_第4张图片
translate.png

根据上面的计算公式,设置tx,ty的值就直接相当于设置它的坐标x,y了。通过设置它的坐标我们就可以很轻松的实现它的位移操作了。

CGAffineTransformScale

方法定义:

CGAffineTransform CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy);

学习计划(6) - 动画 - 形变动画_第5张图片
scale.gif

sx,sy的值等于1为不变,小于1为缩小,大于1为放大

CGAffineTransformRotate
用于图形旋转

方法定义
CGAffineTransform CGAffineTransformRotate(CGAffineTransform t, CGFloat angle);

In iOS, a positive value specifies counterclockwise rotation and a negative value specifies clockwise rotation. In macOS, a positive value specifies clockwise rotation and a negative value specifies counterclockwise rotation.
在iOS中,一个正值指定了逆时针旋转,而负值则指定顺时针旋转。在macOS中,一个正的值指定顺时针旋转,一个负的值指定逆时针旋转。

官网的翻译意思是这样的。然后经过试验,在iOS中 正数是顺时针,负数才是逆时针的。我是不是该提点建议啥的,在线等,蛮急的。

case GWAnimationRotatingClockwise://顺时针
            self.showView.transform = CGAffineTransformRotate(self.showView.transform, 90);
            break;
case GWAnimationRotatingAnticlockwise://逆时针
            self.showView.transform = CGAffineTransformRotate(self.showView.transform, -90);
            break;

其原理图如下:


旋转矩阵.gif
旋转计算.gif

填值参考:


学习计划(6) - 动画 - 形变动画_第6张图片
弧线参考图.png
学习计划(6) - 动画 - 形变动画_第7张图片
rotate.gif
CGAffineTransformInvert
反转当前图形的坐标

Inversion is generally used to provide reverse transformation of points within transformed objects. Given the coordinates (x,y), which have been transformed by a given matrix to new coordinates (x’,y’), transforming the coordinates (x’,y’) by the inverse matrix produces the original coordinates (x,y).
反转通常用于提供转换对象内的点的反向转换。给定的坐标(x,y)已经被一个给定的矩阵转换为新的坐标(x',y'),通过逆矩阵变换坐标(x',y')产生原始坐标(x,y)。

学习计划(6) - 动画 - 形变动画_第8张图片
invert.gif
CGAffineTransformConcat
将两个transform组合在一起。

方法定义
CGAffineTransform CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);
返回一个新的transform: t’ = t1*t2.

学习计划(6) - 动画 - 形变动画_第9张图片
合并.gif

注意,矩阵运算不是交换——你连接矩阵的顺序是很重要的。也就是说,乘以矩阵t1乘以矩阵t2的结果并不一定等于乘以矩阵t1乘以矩阵t1。

Evaluating Affine Transforms
判断类型

CGAffineTransformIsIdentity
检查仿射转换是否为恒等变换。
CGAffineTransformEqualToTransform
检查两个仿射变换是否相等。

了解了API之后,我们就开始实践了:
设置枚举值,我们可以方便的使用

typedef NS_ENUM(NSInteger,GWDeformationType){
    GWAnimationDisplacementUp,//位移
    GWAnimationDisplacementDown,
    GWAnimationDisplacementLeft,
    GWAnimationDisplacementRight,
    GWAnimationZoomEnlargement,        //缩放
    GWAnimationZoomOut,
    GWAnimationRotatingClockwise,    //旋转
    GWAnimationRotatingAnticlockwise,
    GWAnimationDeformation,  //形变
    GWAnimationInvert,       //反转
    GWAnimationConcat       //合并
};

调用API就很简单了:

        case GWAnimationDisplacementUp://上移
            self.showView.transform  = CGAffineTransformTranslate(self.showView.transform, 0,-10);
            break;
        case GWAnimationDisplacementDown://下移
            self.showView.transform  = CGAffineTransformTranslate(self.showView.transform, 0,10);
            break;
        case GWAnimationDisplacementLeft://左移
            self.showView.transform  = CGAffineTransformTranslate(self.showView.transform, -10,0);
            break;
        case GWAnimationDisplacementRight://右移
            self.showView.transform  = CGAffineTransformTranslate(self.showView.transform, 10,0);
            break;
        case GWAnimationZoomEnlargement: //放大
            self.showView.transform = CGAffineTransformScale(self.showView.transform,1.5,1.5);
            break;
        case GWAnimationZoomOut://缩小
            self.showView.transform = CGAffineTransformScale(self.showView.transform,0.5,0.5);
            break;
        case GWAnimationRotatingClockwise://顺时针
            self.showView.transform = CGAffineTransformRotate(self.showView.transform, 90);
            break;
        case GWAnimationRotatingAnticlockwise://逆时针
            self.showView.transform = CGAffineTransformRotate(self.showView.transform, -90);
            break;

单独的使用并无什么乐趣,我们可以做个简单的动画如下图所示:


学习计划(6) - 动画 - 形变动画_第10张图片
形变.gif
[UIView animateWithDuration:1 animations:^{
                self.showView.transform = CGAffineTransformMakeScale(1.5,1.5);
     } completion:^(BOOL finished) {
                [UIView animateWithDuration:1 animations:^{
                    self.showView.transform = CGAffineTransformScale(self.showView.transform,0.5,0.5);
                    self.showView.transform = CGAffineTransformRotate(self.showView.transform, 180);
                } completion:^(BOOL finished) {
                    [UIView animateWithDuration:1 animations:^{
                        self.showView.transform = CGAffineTransformScale(self.showView.transform,2,2);
                        self.showView.transform = CGAffineTransformRotate(self.showView.transform, -180);
                    } completion:^(BOOL finished) {
                    }];
                }];
            }];

DEMO:
https://github.com/yanggenwei/GWAnimation/tree/master

你可能感兴趣的:(学习计划(6) - 动画 - 形变动画)