iOS动画和特效:仿射变换-CGAffineTransform

仿射变换 AffineTransform,在iOS中的实现类是CGAffineTransformCATransform3D,很多动画效果都需要用到仿射去完成,可以说仿射是动画基础

目录



<a name="what-pane"></a>仿射是什么

在iOS的CGAffineTrans的API中,封装了几个好用的API去实现仿射变换的效果

    //位移仿射
    CGAffineTransformMakeTranslation
    CGAffineTransformTranslate
    //旋转仿射
    CGAffineTransformMakeRotation
    CGAffineTransformRotate
    //缩放仿射
    CGAffineTransformMakeScale
    CGAffineTransformScale
    //叠加仿射效果
    CGAffineTransformConcat
    //CATransform3D也对应一组相似的api,这个后面在研究到它的时候仔细说说

make 的方法是直接返回一个仿射变换效果,不带 make 方法是用来给已有的仿射效果叠加效果,类似于CGAffineTransformConcat方法的作用。

在大多数情况下用这几个封装好的仿射方法基本就能实现大多数的需求。但是既然是研究仿射,那大家可以看 CGAffineTransform最基础的那个仿射方法的使用,能理解这个方法的使用,基本上就知道仿射是个什么意思了。

 CGAffineTransformMake ( CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty )

这个方法的6个参数可以拼出一个矩阵

    //仿射矩阵
    [         ]
      a  b  0
      c  d  0
      tx ty 1
    [         ]

仿射变化的定义有点复杂,我自己理解就是: 点 p(以二维坐标为例)通过仿射矩阵C 后变成新的点 p'

以上面的缩放仿射变化CGAffineTransformScale为例。就是view中每一个点 p 通过矩阵C后,view中的每一个新的点 p' 组成的新的图形,相比之前的view有了缩放的效果。

  • 仿射变换的原理和计算

仿射变化原理是数学中的矩阵原理(线性代数、矩阵分析。感慨之前为什么没好好学这门课。。。 自己看这个仿射矩阵画了好多时间才弄明白怎么回事,还补了补基础的矩阵计算知识),要弄明白仿射矩阵对作用点的影响,还得先看看矩阵的乘法怎么计算。

  • 基础-矩阵的乘法

矩阵A = [      ]
          1  2 
          3  0
        [      ]

 矩阵B = [         ]
          0  2  3
          1  1  2
        [         ]

 矩阵C = A * B =     [         ]
                      2  4  7
                      0  6  9
                    [         ]

计算过程:

矩阵C = A * B =      [                                 ]      =        [          ] 
                      (1*0+2*1)  (1*2+2*1)  (1*3+2*2)                    2  4  7
                      (3*0+0*1)  (3*2+0*1)  (3*3+0*2)                    0  6  9
                    [                                 ]               [           ]
计算规则:
  1. 当矩阵A的列数等于矩阵B的行数是,才可以计算
  2. 计算的结果矩阵C的行数等于A的行数,列数等于B的列数
  3. 结果矩阵C的第 i 行第 j 列的元素Cij 等于矩阵A的第 i 行的元素与矩阵B的第 j 列对应元素乘积之和

仿射变换的矩阵计算

仿射计算中,(以二维坐标为例,坐标点为x,y)我们设我们的坐标点矩阵为

A = [x y 1]

仿射变换基础矩阵为:

 //仿射基础矩阵
    B = [         ]
          a  b  0
          c  d  0
          tx ty 1
        [         ]

根据矩阵计算规则我们知道A x B的结果是一个1行3列的矩阵,设A x B得到的新矩阵C ,那么C的矩阵应该为

 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

看明白了嘛?这步很关键。根据这个公式,那么仿射矩阵就可以分成5种分别对应

12.jpg
34.jpg

<a name="ktranslation"></a>平移(Translation)

设a,d=1 c,b = 0 那么

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

就变成了

x' = x + tx
y' = y + ty

把a,b,c,d带入仿射的基础矩阵,就得了仿射的位移矩阵

        //仿射基础矩阵
        [         ]                        [          ]
          a  b  0                             1  0  0
          c  d  0              =              0  1  0
          tx ty 1                             tx ty 1
        [         ]                        [          ]

再来看看代码

//向右移动300的仿射效果
let translate = CGAffineTransformMakeTranslation(300, 0)

//使用仿射基础方法CGAffineTransformMake ( CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty )
let translate = CGAffineTransformMake(1,0,0,1,300,0)

<a name="kscale"></a> 缩放(Scale)

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

设 c,b,tx,ty = 0 ,得到

x' = a*x 
y' = d*y 

代码

//x和y都放大1倍
//let scaleAffine = CGAffineTransformMakeScale(2, 2)

//使用仿射基础方法CGAffineTransformMake 
let scaleAffine = CGAffineTransformMake(2,0,0,2,0,0)

<a name="kshear"></a>剪切(Shear)

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

设 a,d = 1 tx,ty = 0 ,得到

x' = x + cy
y' = y + bx 

x和y的变化总是相互关联,这个就是 剪切(Shear)

代码

//使用仿射基础方法CGAffineTransformMake,设置x和y都为0.5的斜切
//斜切效果只能用CGAffineTransformMake实现,但是通过CATransform3DMakeRotation可以有类似的效果
let shearAffine = CGAffineTransformMake(1,0.5,0.5,1,0,0)

<a name="krotation" ></a>旋转(Rotation)

       //仿射旋转矩阵
       [                    ]
          cos a   sin a   0
         -sin a   cos a   0
           0      0       1
       [                    ]

绕坐标原点逆时针旋转 θ 度,则 p'=(x·cosθ-y·sinθ, x·sinθ+y·cosθ, 1)

                                                              [              ]                             
                                                               cosθ  sinθ  0       
p’ = (x * cosθ - y*sinθ , x*sinθ + y*cosθ, 1) =   (x,y,1)*     -sinθ  cosθ  0
                                                                0     0     1
                                                              [              ]                             

p'=(x·cosθ-y·sinθ, x·sinθ+y·cosθ, 1)推导公式过程
从数学上来说,此公式可以用来计算某个点绕另外一点旋转一定角度后的坐标,例如:A(x,y)绕B(a,b)旋转β度后的位置为C(c,d),则x,y,a,b,β,c,d有如下关系式

2010080600581929.jpg
  1. 设A点旋转前的角度为δ,则旋转(逆时针)到C点后角度为δ+β
  1. 显然r=x/cos(δ)=y/sin(δ)=d/sin(δ+β)=c/cos(δ+β)
  2. 由三角函数两角和差公式知:
    sin(δ+β) = sin(δ)cos(β) + cos(δ)sin(β)
    cos(δ+β) = cos(δ)cos(β) - sin(δ)sin(β)
    所以得出:
d = r*sin(δ+β) = r*sin(δ)cos(β) + r*cos(δ)sin(β) = ycos(β)+xsin(β)```

---
代码

let rotation = CGAffineTransformMake(CGFloat( cos(M_PI_4) ), CGFloat( sin(M_PI_4) ), -CGFloat( sin(M_PI_4) ), CGFloat( cos(M_PI_4) ), 0, 0)

### <a name="kflip"></a>翻转(Flip)
我阅读的所有Flip效果都是使用 ```CATransform3D```实现的,代码如下

//Flip仿射,要是有3D去实现
let flip = CATransform3DMakeRotation(angle, x, y, z)

//示例
let flipX = CATransform3DMakeRotation(CGFloat(M_PI), 1, 0, 0)
let flipY = CATransform3DMakeRotation(CGFloat(M_PI), 0, 1, 0)
let flipZ = CATransform3DMakeRotation(CGFloat(M_PI), 0, 0, 1)

---
> 总结一下```CATransform3DMakeRotation```方法的6个参数

在不考虑旋转时,```CATransform3DMakeRotation``` 6个参数可以写成

//sx,sy:缩放因子
//shx,shy:斜切因子
//tx,ty:移动因子
CGAffineTransformMake (sx,shx,shy,sy,tx,ty)

仿射在iOS中常用的方法

//位移仿射
CGAffineTransformMakeTranslation
CGAffineTransformTranslate
//旋转仿射
CGAffineTransformMakeRotation
CGAffineTransformRotate
//缩放仿射
CGAffineTransformMakeScale
CGAffineTransformScale
//叠加仿射效果
CGAffineTransformConcat
//仿射矩阵方法,可以直接做效果叠加
CGAffineTransformMake (sx,shx,shy,sy,tx,ty)
/*
这个是一个初始化矩阵,带入矩阵算法计算后的结构会得到
x'=x , y'=y
它的作用是清除之前对矩阵设置的仿射效果,或者用来初始化一个原始无效果的仿射矩阵
[ 1 0 0 ]
[ 0 1 0 ]
[ 0 0 1 ]
*/
CGAffineTransformIdentity
//检查是否有做过仿射效果
CGAffineTransformIsIdentity(transform)
//检查2个仿射效果是否相同
CGAffineTransformEqualToTransform(transform1,transform2)
//仿射效果反转(反效果,比如原来扩大,就变成缩小)
CGAffineTransformInvert(transform)

---
###<a name="3D仿射变换"></a>3D仿射变换
类似于2D仿射,3D仿射也有一个基础矩阵,并且比2D的多一个维度

[                       ]
    m11  m12  m13  m14
    m21  m22  m23  m24
    m31  m32  m33  m34
    m41  m42  m43  m44  
[                       ]
矩阵的计算过程和2D类似,最后也能得到矩阵中每个位置的值对3D仿射效果的作用。我直接把结果贴出来。

平移因子: m41(x位置) m42(y位置) m43(z位置) 缩放因子: m11(x位置) m22(y位置)

切变因子: m21(x位置) m12(y位置)

旋转因子: m13(x位置) m31(y位置)

透视因子: m34(有旋转才能看出效果)

####3D仿射常用的方法

//位移3D仿射
CATransform3DMakeTranslation
CATransform3DTranslation
//旋转3D仿射
CATransform3DMakeRotation
CATransform3DRotation
//缩放3D仿射
CATransform3DMakeScale
CATransform3DScale
//叠加3D仿射效果
CATransform3DConcat
//仿射基础3D方法,可以直接做效果叠加
CGAffineTransformMake (sx,shx,shy,sy,tx,ty)
/*
这个是一个初始化矩阵,带入矩阵算法计算后的结构会得到
x'=x , y'=y , z'=z
它的作用是清除之前对矩阵设置的仿射效果,或者用来初始化一个原始无效果的仿射矩阵
[ 1 0 0 0 ]
[ 0 1 0 0 ]
[ 0 0 1 0 ]
[ 0 0 0 1 ]
*/
CATransform3DIdentity
//检查是否有做过仿射3D效果
CATransform3DIsIdentity(transform)
//检查是否是一个仿射3D效果
CATransform3DIsAffine(transform)
//检查2个3D仿射效果是否相同
CATransform3DEqualToTransform(transform1,transform2)
//3D仿射效果反转(反效果,比如原来扩大,就变成缩小)
CATransform3DInvert(transform)
//2D仿射转换3D仿射
CATransform3DGetAffineTransform(transform)
CATransform3DMakeAffineTransform(transform)

原文:http://liuyanwei.jumppo.com/2015/11/24/iOS-affine-transfermation-animation.html?utm_source=tuicool&utm_medium=referral
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容