需求:通过 layer 层,实现透视效果。
参考地址:http://geeklu.com/2012/07/ios-3d-perspective/,http://blog.sina.com.cn/s/blog_71715bf801019ut9.html
先看一下原始界面:
经过 rotate 之后:
- (void)viewDidLoad
{
[superviewDidLoad];
CATransform3D rotate = CATransform3DMakeRotation(M_PI/3, 1, 0, 0);
_imageView.layer.transform = rotate;
}
可以看到,iOS 自带的方法中,是通过做正交投影做的 rotate。
通过下面的方法,可以实现透视投影的效果:
CATransform3D CATransform3DMakePerspective(CGPoint center, float disZ)
{
CATransform3D transToCenter = CATransform3DMakeTranslation(-center.x, -center.y, 0);
CATransform3D transBack = CATransform3DMakeTranslation(center.x, center.y, 0);
CATransform3D scale = CATransform3DIdentity;
scale.m34 = -1.0f/disZ;
return CATransform3DConcat(CATransform3DConcat(transToCenter, scale), transBack);
}
CATransform3D CATransform3DPerspect(CATransform3D t, CGPoint center, float disZ)
{
return CATransform3DConcat(t, CATransform3DMakePerspective(center, disZ));
}
CATransform3DPerspect 的作用:(摘自 http://blog.sina.com.cn/s/blog_71715bf801019ut9.html)
这个函数的实现原理要参考计算机图形学的3D变换部分。接口的含义,center指的是相机的位置,相机的位置是相对于要进行变换的CALayer的来说的,原点是CALayer的anchorPoint在整个CALayer的位置,例如CALayer的大小是(100, 200), anchorPoint值为(0.5, 0.5),此时anchorPoint在整个CALayer中的位置就是(50, 100),正中心的位置。传入透视变换的相机位置为(0, 0),那么相机所在的位置相对于CALayer就是(50, 100)。如果希望相机在左上角,则需要传入(-50, -100)。disZ表示的是相机离z=0平面(也可以理解为屏幕)的距离。
CATransform3DPerspect 的使用:
- (void)viewDidLoad
{
[superviewDidLoad];
CATransform3D rotate = CATransform3DMakeRotation(M_PI/3, 1, 0, 0);
_imageView.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 200);
}
效果图:
现在上面的方法实现了透视效果,接着还需要解决另外两个问题:
先来看一下如何设置透视效果的强弱
参考:http://geeklu.com/2012/07/ios-3d-perspective http://blog.sina.com.cn/s/blog_620bf89501011fl8.html http://blog.sina.com.cn/s/blog_620bf89501011g7n.html
由上面的信息可知,将 z 值设置大一些,这透视效果就相对弱一些,如将 z 设置为 1000
- (void)viewDidLoad
{
[superviewDidLoad];
CATransform3D rotate = CATransform3DMakeRotation(M_PI/3, 1, 0, 0);
_imageView.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 1000);
}
效果图如下
要实现绕固定边旋转,只需要调整 layer 的 anchorPoint 就行了
- (void)viewDidLoad
{
[superviewDidLoad];
CALayer *layer = [_imageView layer];
CGPoint oldAnchorPoint = layer.anchorPoint;
[layer setAnchorPoint:CGPointMake(0.5, 1.0)];
[layer setPosition:CGPointMake(layer.position.x + layer.bounds.size.width * (layer.anchorPoint.x - oldAnchorPoint.x), layer.position.y + layer.bounds.size.height * (layer.anchorPoint.y - oldAnchorPoint.y))];
CATransform3D rotate = CATransform3DMakeRotation(M_PI/3, 1, 0, 0);
_imageView.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 1000);
}
下面再把 disZ 设置为 100,看看效果
上图中的图片绕着底边旋转了 60 度,按照勾股定理,上面的边应该是靠在绿色的边上的,但为什么差这么多呢?看一下下面这张图:
由于我移动了 anchorPoint,如上图,现在的观察点在 eye1 上面。以 yz 面上的点为例,在 eye2 处,红色矩形在 xy 面上的投影为 A 点,这也就是为什么旋转之后,矩形在绿色线条下面的原因了。ok,现在把 eye1 往上移动到视图中心即可。
- (void)viewDidLoad
{
[superviewDidLoad];
CALayer *layer = [_imageView layer];
CGPoint oldAnchorPoint = layer.anchorPoint;
[layer setAnchorPoint:CGPointMake(0.5, 1)];
[layer setPosition:CGPointMake(layer.position.x + layer.bounds.size.width * (layer.anchorPoint.x - oldAnchorPoint.x), layer.position.y + layer.bounds.size.height * (layer.anchorPoint.y - oldAnchorPoint.y))];
CATransform3D rotate = CATransform3DMakeRotation(M_PI/180.0*60.0, 1, 0, 0);
_imageView.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, -90), 100);
}