iOS動畫和特效:仿射變換-CGAffineTransform

仿射變換 AffineTransform,在iOS中的實(shí)現(xiàn)類是CGAffineTransformCATransform3D,很多動畫效果都需要用到仿射去完成,可以說仿射是動畫基礎(chǔ)

目錄



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

在iOS的CGAffineTrans的API中,封裝了幾個(gè)好用的API去實(shí)現(xiàn)仿射變換的效果

    //位移仿射
    CGAffineTransformMakeTranslation
    CGAffineTransformTranslate
    //旋轉(zhuǎn)仿射
    CGAffineTransformMakeRotation
    CGAffineTransformRotate
    //縮放仿射
    CGAffineTransformMakeScale
    CGAffineTransformScale
    //疊加仿射效果
    CGAffineTransformConcat
    //CATransform3D也對應(yīng)一組相似的api,這個(gè)后面在研究到它的時(shí)候仔細(xì)說說

make 的方法是直接返回一個(gè)仿射變換效果,不帶 make 方法是用來給已有的仿射效果疊加效果,類似于CGAffineTransformConcat方法的作用。

在大多數(shù)情況下用這幾個(gè)封裝好的仿射方法基本就能實(shí)現(xiàn)大多數(shù)的需求。但是既然是研究仿射,那大家可以看 CGAffineTransform最基礎(chǔ)的那個(gè)仿射方法的使用,能理解這個(gè)方法的使用,基本上就知道仿射是個(gè)什么意思了。

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

這個(gè)方法的6個(gè)參數(shù)可以拼出一個(gè)矩陣

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

仿射變化的定義有點(diǎn)復(fù)雜,我自己理解就是: 點(diǎn) p(以二維坐標(biāo)為例)通過仿射矩陣C 后變成新的點(diǎn) p' 。

以上面的縮放仿射變化CGAffineTransformScale為例。就是view中每一個(gè)點(diǎn) p 通過矩陣C后,view中的每一個(gè)新的點(diǎn) p' 組成的新的圖形,相比之前的view有了縮放的效果。

  • 仿射變換的原理和計(jì)算

仿射變化原理是數(shù)學(xué)中的矩陣原理(線性代數(shù)、矩陣分析。感慨之前為什么沒好好學(xué)這門課。。。 自己看這個(gè)仿射矩陣畫了好多時(shí)間才弄明白怎么回事,還補(bǔ)了補(bǔ)基礎(chǔ)的矩陣計(jì)算知識),要弄明白仿射矩陣對作用點(diǎn)的影響,還得先看看矩陣的乘法怎么計(jì)算。

  • 基礎(chǔ)-矩陣的乘法

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

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

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

計(jì)算過程:

矩陣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
                    [                                 ]               [           ]
計(jì)算規(guī)則:
  1. 當(dāng)矩陣A的列數(shù)等于矩陣B的行數(shù)是,才可以計(jì)算
  2. 計(jì)算的結(jié)果矩陣C的行數(shù)等于A的行數(shù),列數(shù)等于B的列數(shù)
  3. 結(jié)果矩陣C的第 i 行第 j 列的元素Cij 等于矩陣A的第 i 行的元素與矩陣B的第 j 列對應(yīng)元素乘積之和

仿射變換的矩陣計(jì)算

仿射計(jì)算中,(以二維坐標(biāo)為例,坐標(biāo)點(diǎn)為x,y)我們設(shè)我們的坐標(biāo)點(diǎn)矩陣為

A = [x y 1]

仿射變換基礎(chǔ)矩陣為:

 //仿射基礎(chǔ)矩陣
    B = [         ]
          a  b  0
          c  d  0
          tx ty 1
        [         ]

根據(jù)矩陣計(jì)算規(guī)則我們知道A x B的結(jié)果是一個(gè)1行3列的矩陣,設(shè)A x B得到的新矩陣C ,那么C的矩陣應(yīng)該為

 C = [  (a*x+c*y+tx)  (b*x+d*y+ty)  (1) ]

設(shè)C為 = [x' y' 1] , 那么可以得到

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

看明白了嘛?這步很關(guān)鍵。根據(jù)這個(gè)公式,那么仿射矩陣就可以分成5種分別對應(yīng)

12.jpg
34.jpg

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

設(shè)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ǔ),b,c,d帶入仿射的基礎(chǔ)矩陣,就得了仿射的位移矩陣

        //仿射基礎(chǔ)矩陣
        [         ]                        [          ]
          a  b  0                             1  0  0
          c  d  0              =              0  1  0
          tx ty 1                             tx ty 1
        [         ]                        [          ]

再來看看代碼

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

//使用仿射基礎(chǔ)方法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

設(shè) c,b,tx,ty = 0 ,得到

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

代碼

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

//使用仿射基礎(chǔ)方法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

設(shè) a,d = 1 tx,ty = 0 ,得到

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

x和y的變化總是相互關(guān)聯(lián),這個(gè)就是 剪切(Shear)

代碼

//使用仿射基礎(chǔ)方法CGAffineTransformMake,設(shè)置x和y都為0.5的斜切
//斜切效果只能用CGAffineTransformMake實(shí)現(xiàn),但是通過CATransform3DMakeRotation可以有類似的效果
let shearAffine = CGAffineTransformMake(1,0.5,0.5,1,0,0)

<a name="krotation" ></a>旋轉(zhuǎn)(Rotation)

       //仿射旋轉(zhuǎn)矩陣
       [                    ]
          cos a   sin a   0
         -sin a   cos a   0
           0      0       1
       [                    ]

繞坐標(biāo)原點(diǎn)逆時(shí)針旋轉(zhuǎn) θ 度,則 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)推導(dǎo)公式過程
從數(shù)學(xué)上來說,此公式可以用來計(jì)算某個(gè)點(diǎn)繞另外一點(diǎn)旋轉(zhuǎn)一定角度后的坐標(biāo),例如:A(x,y)繞B(a,b)旋轉(zhuǎn)β度后的位置為C(c,d),則x,y,a,b,β,c,d有如下關(guān)系式

2010080600581929.jpg
  1. 設(shè)A點(diǎn)旋轉(zhuǎn)前的角度為δ,則旋轉(zhuǎn)(逆時(shí)針)到C點(diǎn)后角度為δ+β
  1. 顯然r=x/cos(δ)=y/sin(δ)=d/sin(δ+β)=c/cos(δ+β)
  2. 由三角函數(shù)兩角和差公式知:
    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>翻轉(zhuǎn)(Flip)
我閱讀的所有Flip效果都是使用 ```CATransform3D```實(shí)現(xiàn)的,代碼如下

//Flip仿射,要是有3D去實(shí)現(xiàn)
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)

---
> 總結(jié)一下```CATransform3DMakeRotation```方法的6個(gè)參數(shù)

在不考慮旋轉(zhuǎn)時(shí),```CATransform3DMakeRotation``` 6個(gè)參數(shù)可以寫成

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

仿射在iOS中常用的方法

//位移仿射
CGAffineTransformMakeTranslation
CGAffineTransformTranslate
//旋轉(zhuǎn)仿射
CGAffineTransformMakeRotation
CGAffineTransformRotate
//縮放仿射
CGAffineTransformMakeScale
CGAffineTransformScale
//疊加仿射效果
CGAffineTransformConcat
//仿射矩陣方法,可以直接做效果疊加
CGAffineTransformMake (sx,shx,shy,sy,tx,ty)
/*
這個(gè)是一個(gè)初始化矩陣,帶入矩陣算法計(jì)算后的結(jié)構(gòu)會得到
x'=x , y'=y
它的作用是清除之前對矩陣設(shè)置的仿射效果,或者用來初始化一個(gè)原始無效果的仿射矩陣
[ 1 0 0 ]
[ 0 1 0 ]
[ 0 0 1 ]
*/
CGAffineTransformIdentity
//檢查是否有做過仿射效果
CGAffineTransformIsIdentity(transform)
//檢查2個(gè)仿射效果是否相同
CGAffineTransformEqualToTransform(transform1,transform2)
//仿射效果反轉(zhuǎn)(反效果,比如原來擴(kuò)大,就變成縮小)
CGAffineTransformInvert(transform)

---
###<a name="3D仿射變換"></a>3D仿射變換
類似于2D仿射,3D仿射也有一個(gè)基礎(chǔ)矩陣,并且比2D的多一個(gè)維度

[                       ]
    m11  m12  m13  m14
    m21  m22  m23  m24
    m31  m32  m33  m34
    m41  m42  m43  m44  
[                       ]
矩陣的計(jì)算過程和2D類似,最后也能得到矩陣中每個(gè)位置的值對3D仿射效果的作用。我直接把結(jié)果貼出來。

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

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

旋轉(zhuǎn)因子: m13(x位置) m31(y位置)

透視因子: m34(有旋轉(zhuǎn)才能看出效果)

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

//位移3D仿射
CATransform3DMakeTranslation
CATransform3DTranslation
//旋轉(zhuǎn)3D仿射
CATransform3DMakeRotation
CATransform3DRotation
//縮放3D仿射
CATransform3DMakeScale
CATransform3DScale
//疊加3D仿射效果
CATransform3DConcat
//仿射基礎(chǔ)3D方法,可以直接做效果疊加
CGAffineTransformMake (sx,shx,shy,sy,tx,ty)
/*
這個(gè)是一個(gè)初始化矩陣,帶入矩陣算法計(jì)算后的結(jié)構(gòu)會得到
x'=x , y'=y , z'=z
它的作用是清除之前對矩陣設(shè)置的仿射效果,或者用來初始化一個(gè)原始無效果的仿射矩陣
[ 1 0 0 0 ]
[ 0 1 0 0 ]
[ 0 0 1 0 ]
[ 0 0 0 1 ]
*/
CATransform3DIdentity
//檢查是否有做過仿射3D效果
CATransform3DIsIdentity(transform)
//檢查是否是一個(gè)仿射3D效果
CATransform3DIsAffine(transform)
//檢查2個(gè)3D仿射效果是否相同
CATransform3DEqualToTransform(transform1,transform2)
//3D仿射效果反轉(zhuǎn)(反效果,比如原來擴(kuò)大,就變成縮?。?br> CATransform3DInvert(transform)
//2D仿射轉(zhuǎn)換3D仿射
CATransform3DGetAffineTransform(transform)
CATransform3DMakeAffineTransform(transform)

原文:http://liuyanwei.jumppo.com/2015/11/24/iOS-affine-transfermation-animation.html?utm_source=tuicool&utm_medium=referral
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容