仿射變換 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)