正常的图片如下:
绕X轴,经过60度的旋转之后,如图所示:
- (void)transform3d_1
{
// 沿X轴,3d变换60度
CATransform3D rotate = CATransform3DMakeRotation(M_PI/3, 1, 0, 0);
_colorImgView.layer.transform = rotate;
}
下面使用3D变换,相机位置不同时的效果分别如下 :
- (void)transform3d_2
{ // 下面3张图片,分别演示了相机距离图片锚点为40,80,160的情况
CATransform3D rotate = CATransform3DMakeRotation(M_PI/3, 1, 0, 0);//角度代表倒下去的程度,即与竖直面的夹角,0度代表不倒下
_colorImgView.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 80);// 40,80,160
}
/*
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平面(也可以理解为屏幕)的距离。
*/
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));
}
相机距离为40时
相机距离为80时
相机距离为160时
由上面可以知道,相机 距离 ImageView的锚点越近,透视效果越强; 距离越远,透视效果越弱;
如下图所示:
如果要绕底边进行透视,那么需将图层的锚点移动到底边的中点上,如下图所示
- (void)transform3d_3
{ // 有bug
// 如果要绕固定ImgeView的边(如底边)旋转,只需要调整 layer 的 anchorPoint 到对应的边上就行了
CALayer *layer = [_colorImgView layer];
CGPoint oldAnchorPoint = layer.anchorPoint;
// sg__old anchor point:0.500000,0.500000
NSLog(@"sg__old anchor point:%f,%f",oldAnchorPoint.x,oldAnchorPoint.y);
// 向下,向右 为正;此时锚点在底边中点
[layer setAnchorPoint:CGPointMake(0.5, 1.0)];
// 根据新旧锚点,移动图层的坐标位置
// sg__layer pos old__187.000000,333.000000
NSLog(@"sg__layer pos old__%f,%f",layer.position.x,layer.position.y);
CGFloat posX = layer.position.x + layer.bounds.size.width * (layer.anchorPoint.x - oldAnchorPoint.x);
CGFloat posY = layer.position.y + layer.bounds.size.height * (layer.anchorPoint.y - oldAnchorPoint.y);
// sg__layer pos new__187.000000,453.000000
NSLog(@"sg__layer pos new__%f,%f",posX,posY);
[layer setPosition:CGPointMake(posX,posY)];
CATransform3D rotate = CATransform3DMakeRotation(M_PI/3, 1, 0, 0);
_colorImgView.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 160);
}
上面的有一个bug,现加以解释说明并修正
为什么只是绕底边旋转了60度,会看起来这么扁,按照勾股定理应该
高只有长的1/2呀,而现在是长480,高只有100,理论上变形后的高度应该有240的
如下图所示这是因为移动了imgView的anchorPoint,从0.5,0.5向下移动到了0.5,1.0,
也就是相机(观察点)现在在 eye1处了。
以 yz 面上的点为例,在 eye2 处,红色矩形在 xy 面上的投影为比较靠下的 A 点,
这也就是为什么旋转之后,矩形的高度比较小的原因了(不足宽度的1/2)
ok,现在只需要把 eye1 往上移动到视图中心即可。即往上移动240/2=120
修正后的代码及效果图如下:
- (void)transform3d_4
{
// 如果要绕固定ImgeView的边(如底边)旋转,只需要调整 layer 的 anchorPoint 到对应的边上就行了
CALayer *layer = [_colorImgView layer];
CGPoint oldAnchorPoint = layer.anchorPoint;
// sg__old anchor point:0.500000,0.500000
NSLog(@"sg__old anchor point:%f,%f",oldAnchorPoint.x,oldAnchorPoint.y);
// 向下,向右 为正;此时锚点在底边中点
[layer setAnchorPoint:CGPointMake(0.5, 1.0)];
// 根据新旧锚点,移动图层的坐标位置
// sg__layer pos old__187.000000,333.000000
NSLog(@"sg__layer pos old__%f,%f",layer.position.x,layer.position.y);
CGFloat posX = layer.position.x + layer.bounds.size.width * (layer.anchorPoint.x - oldAnchorPoint.x);
CGFloat posY = layer.position.y + layer.bounds.size.height * (layer.anchorPoint.y - oldAnchorPoint.y);
// sg__layer pos new__187.000000,453.000000
NSLog(@"sg__layer pos new__%f,%f",posX,posY);
[layer setPosition:CGPointMake(posX,posY)];
CATransform3D rotate = CATransform3DMakeRotation(M_PI/3, 1, 0, 0);
// 由于上面整个图层向底边移动了,所以此时,这个相机位置的Y值,应该上移一段距离,到中心位置;240为图片的高
_colorImgView.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, -240/2.0), 160);
}
那么问题来了,如果要绕左边,类似于开门关门进行透视呢?即相机 沿Y轴方向3D变换,那么只要修改一下参数即可
照猫画虎做一遍
#pragma mark - 绕Y轴变换
- (void)transform3d_Y_1
{
// 沿Y轴,3d变换60度
CATransform3D rotate = CATransform3DMakeRotation(M_PI/3, 0, 1, 0);
_colorImgView.layer.transform = rotate;
}
- (void)transform3d_Y_2
{
// 下面3张图片,分别演示了相机距离图片锚点为40,80,160的情况
CATransform3D rotate = CATransform3DMakeRotation(M_PI/3, 0, 1, 0);//角度代表倒下去的程度,即与竖直面的夹角,0度代表不倒下
_colorImgView.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 160);// 40,80,160
}
相机距离40时:
相机距离80时:
相机距离160时:
// 有bug
- (void)transform3d_Y_3
{
// 如果要绕固定ImgeView的边(如左边)旋转,只需要调整 layer 的 anchorPoint 到对应的边上就行了
CALayer *layer = [_colorImgView layer];
CGPoint oldAnchorPoint = layer.anchorPoint;
// sg__old anchor point: 0.5 , 0.5
NSLog(@"sg__old anchor point:%f,%f",oldAnchorPoint.x,oldAnchorPoint.y);
// 向下,向右 为正;此时锚点在左边中点
[layer setAnchorPoint:CGPointMake(0, 0.5)];
// 根据新旧锚点,移动图层的坐标位置
// sg__layer pos old__187.000000,333.000000
NSLog(@"sg__layer pos old__%f,%f",layer.position.x,layer.position.y);
CGFloat posX = layer.position.x + layer.bounds.size.width * (layer.anchorPoint.x - oldAnchorPoint.x);
CGFloat posY = layer.position.y + layer.bounds.size.height * (layer.anchorPoint.y - oldAnchorPoint.y);
// sg__layer pos new__67.000000,333.000000
NSLog(@"sg__layer pos new__%f,%f",posX,posY);
[layer setPosition:CGPointMake(posX,posY)];
CATransform3D rotate = CATransform3DMakeRotation(M_PI/3, 0, 1, 0);
_colorImgView.layer.transform = CATransform3DPerspect(rotate, CGPointMake(0, 0), 160);// 这儿有bug
}
解决上述bug,即由于图层整个左移了,所以相机要向右移动一段距离,对齐到视图的中心
- (void)transform3d_Y_4
{// 如果要绕固定ImgeView的边(如底边)旋转,只需要调整 layer 的 anchorPoint 到对应的边上就行了
CALayer *layer = [_colorImgView layer];
CGPoint oldAnchorPoint = layer.anchorPoint;
// sg__old anchor point:0.500000,0.500000
NSLog(@"sg__old anchor point:%f,%f",oldAnchorPoint.x,oldAnchorPoint.y);
// 向下,向右 为正;此时锚点在左边中点
[layer setAnchorPoint:CGPointMake(0, 0.5)];
// 根据新旧锚点,移动图层的坐标位置
// sg__layer pos old__187.000000,333.000000
NSLog(@"sg__layer pos old__%f,%f",layer.position.x,layer.position.y);
CGFloat posX = layer.position.x + layer.bounds.size.width * (layer.anchorPoint.x - oldAnchorPoint.x);
CGFloat posY = layer.position.y + layer.bounds.size.height * (layer.anchorPoint.y - oldAnchorPoint.y);
// sg__layer pos new__67.000000,333.000000
NSLog(@"sg__layer pos new__%f,%f",posX,posY);
[layer setPosition:CGPointMake(posX,posY)];
CATransform3D rotate = CATransform3DMakeRotation(M_PI/3, 0, 1, 0);
// 由于上面整个图层向左边移动了,所以此时,这个相机位置的X值,应该右移一段距离,到中心位置;240为图片的宽
_colorImgView.layer.transform = CATransform3DPerspect(rotate, CGPointMake(240/2.0, 0), 160);
}