仿射变换 AffineTransform,在iOS中的实现类是CGAffineTransform和CATransform3D,很多动画效果都需要用到仿射去完成,可以说仿射是动画基础
目录
<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
[ ] [ ]
计算规则:
- 当矩阵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种分别对应
<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有如下关系式
- 设A点旋转前的角度为δ,则旋转(逆时针)到C点后角度为δ+β
- 显然
r=x/cos(δ)=y/sin(δ)=d/sin(δ+β)=c/cos(δ+β)
- 由三角函数两角和差公式知:
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