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

推荐阅读更多精彩内容