iOS動畫和特效(七)仿射變換-CGAffineTransform

仿射變換 AffineTransform,在iOS中他的實現類是CGAffineTransform和CATransform3D,很多動畫效果都需要用到仿射去完成 所以仿射是動畫基礎,不能熟練使用也肯定玩不好動畫特效的

在iOS動畫和特效專題(六)中有用到仿射變換的內容,這一篇專門來研究一下仿射變換,已經在iOS中的使用。在我寫這章內容前我也 對仿射變換一無所知,也是查了一下資料作和看了許多別人的解釋,才慢慢了解。和大家一起學習,若有寫的不正確的地方也歡迎指出。

table of content

仿射是什么

仿射變換的原理和計算

仿射在iOS中常用的方法

3D仿射變換

仿射是什么

先看看上一篇文章中用到的仿射方法作為例子

//Srping fall動畫效果實現

//使用了CGAffineTransformMakeTranslation設置y軸的位移

//使用了CGAffineTransformMakeRotation 做了旋轉

//使用了CGAffineTransformConcat合并了位移和旋轉仿射效果

UIView.animateWithDuration(NSTimeInterval(duration), delay: NSTimeInterval(delay), usingSpringWithDamping: damping, initialSpringVelocity: velocity, options: [.CurveLinear,.AllowUserInteraction], animations: { () -> Void in

let translate = CGAffineTransformMakeTranslation(0, 300)

let rotate = CGAffineTransformMakeRotation(15 * CGFloat(M_PI / 180))

self.view.transform = CGAffineTransformConcat(translate, rotate)

}, completion: nil)

//Srping squeezeRight 動畫效果實現

//使用了CGAffineTransformMakeTranslation設置x軸的位移

//使用了CGAffineTransformMakeScale 做了縮放

//使用了CGAffineTransformConcat合并了位移和縮放仿射效果

UIView.animateWithDuration(NSTimeInterval(duration), delay: NSTimeInterval(delay), usingSpringWithDamping: damping, initialSpringVelocity: velocity, options: [.CurveLinear,.AllowUserInteraction], animations: { () -> Void in

let translate = CGAffineTransformMakeTranslation(100, 0)

//slide開頭就不需要scale效果

let scale = CGAffineTransformMakeScale(0.8, 1)

self.view.transform = CGAffineTransformConcat(translate, scale)

}, completion: nil)

好像挺容易的,就是iOS的CGAffineTrans的API,沒錯,iOS封裝了幾個好用的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? 1

2? 0

[? ? ? ]

矩陣B = [? ? ? ? ]

0? 2? 3

1? 1? 2

[? ? ? ? ]

矩陣C = A * B =? ? [? ? ? ? ]

1? 3? 5

0? 4? 6

[? ? ? ? ]

計算過程:

矩陣C = A * B =? ? ? [? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ]? ? ? =? ? ? ? [? ? ? ? ? ]

(1*0+1*1)? (1*2+1*1)? (1*3+1*2)? ? ? ? ? ? ? ? ? ? 1? 3? 5

(2*0+0*1)? (2*2+0*1)? (2*3+0*2)? ? ? ? ? ? ? ? ? ? 0? 4? 6

[? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ]? ? ? ? ? ? ? [? ? ? ? ? ]

=

計算規則:

:當矩陣A的列數等于矩陣B的行數是,才可以計算

:計算的結果矩陣C的行數等于A的行數,列數等于B的列數

:結果矩陣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種分別對應

平移(Translation)

縮放(Scale)

翻轉(Flip)

旋轉(Rotation)

剪切(Shear)

平移(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

這個不就是新的點P'(x' y') 是根據原來的P在x和y分別添加了一個常量就可以得到了嘛? 所以這個就是仿射中的平移效果了。把a,b,c,d帶入仿射的基礎矩陣,就得了仿射的位移矩陣

//仿射基礎矩陣

[? ? ? ? ]

a? b? 0

c? d? 0

tx ty 1

[? ? ? ? ]

//仿射位移矩陣

[? ? ? ? ? ]

1? 0? 0

0? 1? 0

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)

這下應該能明白仿射基礎矩陣的意思了,如果沒明白,從頭在看一遍吧

縮放(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')和原坐標是倍數關系,這個就可以用來做縮放效果了。所以得到縮放矩陣

//仿射縮放矩陣

[? ? ? ? ? ]

a? 0? 0

0? d? 0

0? 0? 1

[? ? ? ? ? ]

//也可以寫成

[? ? ? ? ? ? ]

sx? 0? 0

0? sy? 0

0? 0? 1

[? ? ? ? ? ? ]

代碼

//x和y都放大1倍

//let scaleAffine = CGAffineTransformMakeScale(2, 2)

//使用仿射基礎方法CGAffineTransformMake

let scaleAffine = CGAffineTransformMake(2,0,0,2,0,0)

剪切(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)

//仿射剪切矩陣

[? ? ? ? ? ]

1? b? 0

c? 1? 0

0? 0? 1

[? ? ? ? ? ]

//也可以寫成

[? ? ? ? ? ? ]

1? shx? 0

shy? 1? 0

0? ? 0? 1

[? ? ? ? ? ? ]

代碼

//使用仿射基礎方法CGAffineTransformMake,設置x和y都為0.5的斜切

//斜切效果只能用CGAffineTransformMake實現,但是通過CATransform3DMakeRotation可以有類似的效果

let shearAffine = CGAffineTransformMake(1,0.5,0.5,1,0,0)

旋轉(Rotation)

//仿射旋轉矩陣

[? ? ? ? ? ? ? ? ? ]

cosa? sina? 0

-sina? cosa? 0

0? ? ? 0? ? 1

[? ? ? ? ? ? ? ? ? ]

代碼

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)

翻轉(Flip)

我不知道使用CGAffineTransformMake()如何設置矩陣可以實現翻轉效果,請知道的讀者告知。我閱讀的所有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個參數

在不考慮旋轉時,CATransform3DMakeRotation6個參數可以寫成

//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)

3D仿射變換

3D仿射在iOS中是通過CATransform3D實現的,它有著與CGAffineTrans類似的一組API,但他們有個重要的區別在于CATransform3D的效果只能加在layer的transform屬性上,而CGAffineTransform直接加在View上

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)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容