iOS核心动画笔记5-变换

变换

这一节主要讲的是, 对图层进行旋转,缩放扭曲等操做.app

1. 仿射变换

在视图层面上, UIView有个属性叫作 transform, 能够进行二维层面上的图层变换. 主要包括: 旋转/平移/缩放操做.框架

当图层应用变换矩阵进行变换时候, 图层矩形内的每一个点都会被相应的进行变化, 从而造成一个新的四边形的形状. 仿射变换中"仿射"的意思是不管变换矩阵用什么值, 图层中平行的两条线在变换以后仍然保持平行.ide

2. 建立仿射变换CGAffineTransform

CGAffineTransform其实是一个3*2的矩阵, 在OC中用结构体表示, (CG前缀表示属于Core Graphcs框架), CG框架提供了方便的方法来建立CGAffineTransform结构体, 以下三个是建立旋转, 缩放,平移的方法, 返回值是CGAffineTransform类型结构体.测试

CGAffineTransformMakeRotation(CGFloat angle) 
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)

UIView用来作变换的属性叫作transform, 对应CALayer的属性是affineTransform. CALayer也有一个transform属性, 可是是CATransform3D类型的, 是用来作3D变换的属性.3d

弧度和角度的转换code

#define RADIANS_TO_DEGREES(x) ((x)/M_PI*180.0) 
#define DEGREES_TO_RADIANS(x) ((x)/180.0*M_PI)

3. 混合变换

经过CG框架咱们能够简单的建立不一样的仿射变换, 若是咱们须要缩放的同时又要旋转, 这时候就用到了混合变换, CG框架也提供了丰富的框架, 很简单, 不解释:orm

CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)     
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)      
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)
CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);

CGAffineTransform 空值状态 :CGAffineTransformIdentity.对象

当使用混合变换时候须要注意, 混合变换的顺序会影响结果, 也就是说, 旋转以后平移和平移以后旋转的结果极可能不同. 缘由是上一个变换的变换结果会直接影响以后的变换. 好比先缩放0.5再平移200, 实际上只会平移100, 缘由是缩放时候, 平移也会被缩放掉.blog

4. 剪切变换

Core Graphics提供了计算变换矩阵的一些方法, 通常咱们不直接使用CGAffineTransform的值, 可是若是须要建立斜切的变换, Core Graphics并无提供相应的方法, 这就须要咱们本身直接修改结构体相应的值了.图片

@implementation ViewController

CGAffineTransform CGAffineTransformMakeShear(CGFloat x, CGFloat y)
{
    CGAffineTransform transform = CGAffineTransformIdentity;
    transform.c = -x;
    transform.b = y;
    return transform;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    //shear the layer at a 45-degree angle
    self.layerView.layer.affineTransform = CGAffineTransformMakeShear(1, 0);
}

@end

5. 3D变换

CALayer的transform属性(CATransform3D类型)可让图层在3D空间内移动或者旋转. 和CGAffineTransform相似,CATransform3D也是一个矩阵,CATransform3D是一个能够在3维空间内作变换的4x4的矩阵.

Core Animation提供一系列方法建立CATransform3D类型矩阵. 以下所示, 固然, 也有相应的混合变换的方法.

CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz) 
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)

使用demo:

//2.1 旋转
//    CATransform3D transform3d = CATransform3DMakeRotation(M_PI_4, 1, 0, 0);
    
    //2.2 缩放  可是z轴上的缩放是什么鬼 ?
//    CATransform3D transform3d = CATransform3DMakeScale(2, 1, 1);
    
    //2.3 平移
//    CATransform3D transform3d = CATransform3DMakeTranslation(100, 100, 0);
    
    //2.4 透视投影  测试证实,必须先设置m34的值, 而后再设置旋转角度, 这样才会有透视投影的效果
    //create a new transform
    CATransform3D transform3d = CATransform3DIdentity;
    //apply perspective
    transform3d.m34 = - 1.0 / 500.0;
    //rotate by 45 degrees along the Y axis
    transform3d = CATransform3DRotate(transform3d, M_PI_4, 0, 1, 0);

6. 透视投影

须要咱们手动的改变m34值, 默认值是0, 通常咱们经过设置m34为 -1/d 来应用透视效果(d通常取值500~1000之间),

demo:

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //create a new transform
    CATransform3D transform = CATransform3DIdentity;
    //apply perspective
    transform.m34 = - 1.0 / 500.0;
    //rotate by 45 degrees along the Y axis
    transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
    //apply to layer
    self.layerView.layer.transform = transform;
}

@end

测试发现必须先设置m34值, 而后再进行变换, 不然无效, 暂时还不知道缘由.

效果:

7. 灭点

当在透视角度绘图时候, 原理相机的屋里将会越变越小, 当远到必定的距离, 就缩小成一个点了, 因而全部的物体都聚集消失在一个点, 这个点被称为 灭点. Core Animation定义这个点anchorPoint. 为了让全部图层更有3D效果, 咱们能够将图层都放在同一个位置, 也就是 amchorPoint是相同的, 而后经过变换将它移动到指定位置(而不是直接更改Position), 这样全部的3D图层就都共享一个灭点了.

8. sublayerTransform属性

CALayer有个属性交sublayerTransform, 它是CATransform3D类型, 它可以影响到它全部的直接子图层. 就是说能够一次性对这些图层的容器作变换, 而后全部的子图层都集成这个变换方法.

经过在一个地方设置透视变换会变得很方便, 同时也会带来另外一个好处: 灭点被设置在容器图层的中点, 从而不须要对子图层分别设置了. 意味着, 你能够随意使用position和frame来放置子图层, 而不须要把它们放置在屏幕中央, 而后为了保证统一的灭点用变换来平移.

demo:

#pragma mark - 测试经过 sublayerTransform 属性设置统一的灭点
- (void)testSublayerTransform {
    
    UIView *bgview = [[UIView alloc] initWithFrame:CGRectMake(20, 100, 250, 200)];
    [self.view addSubview:bgview];
    bgview.backgroundColor = [UIColor lightGrayColor];
    
    CATransform3D transform3D = CATransform3DIdentity;
    transform3D.m34 = -1 / 500.0;
    
    bgview.layer.sublayerTransform = transform3D;
    
    
    CALayer *leftLayer = [CALayer layer];
    leftLayer.backgroundColor = [UIColor redColor].CGColor;
    [bgview.layer addSublayer:leftLayer];
    leftLayer.frame = CGRectMake(10, 10, 80, 100);
    leftLayer.transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
    
    CALayer *rightLayer = [CALayer layer];
    rightLayer.backgroundColor = [UIColor redColor].CGColor;
    [bgview.layer addSublayer:rightLayer];
    rightLayer.frame = CGRectMake(CGRectGetWidth(bgview.frame)-90, 10, 80, 100);
    rightLayer.transform = CATransform3DMakeRotation(-M_PI_4, 0, 1, 0);
    
}

9. 背面

经过3D旋转, 咱们能够将图形旋转到背面, 进而去观察图层的背面. 旋转180°时候, 会发现是一个正面的镜像图片, 就是说图层是双面绘制的. 可是, 既然背面永远不会被看到, 为何要去绘制呢???

CALayer有个叫doubleSided的属性来控制图层的背面是否要被绘制. 这是一个BOOL类型, 默认是YES, 若是设置为NO, 那么当图层正面从相机视角消失的时候, 它将不会被绘制.

10. 扁平化图层

内图层向右旋转45°, 外图层向左旋转45°效果:

透视效果+内图层绕y轴旋转, 外图层也绕y轴旋转, 效果以下:

由于, 尽管Core Animation图层存在于3D空间以内, 可是它们不属于同一个3D空间. 每一个图层的3D场景实际上是扁平化的, 当你从正面观察一个图层, 看到的其实是由子图层建立的想象出来的3D场景, 但当你倾斜时候回发现, 这个3D场景牢牢是被绘制在图层的表面了.

这个特性使Core Animation建立复杂3D场景变得十分困难, 你不能使用图层🌲建立一个3D结构的层级关系, 在相同的场景下的任何3D表面必须和一样的图层保持一致, 这是由于每一个父视图都把它的子视图扁平化了. CALayer有个叫 CATransformLayer 的子类用来解决这个问题, 详见后文.

11. 固体对象

测试demo:

#pragma mark - 测试固体
- (void)testSolid {
    
    //
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = -1.0 / 500.0;
    perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 1, 0);
    self.contenterView.layer.sublayerTransform = perspective;
    
    CATransform3D transform;
    
    // 1
    UIView *first = self.faces[0];
    transform = CATransform3DMakeTranslation(0, 0, 100);
    first.layer.transform = transform;
    
    //2
    UIView *second = self.faces[1];
    transform = CATransform3DMakeTranslation(100, 0, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
    second.layer.transform = transform;
    
    //3
    UIView *third = self.faces[2];
    transform = CATransform3DMakeTranslation(-100, 0, 0);
    transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);
    third.layer.transform = transform;
    
    //4
    UIView *forth = self.faces[3];
    transform = CATransform3DMakeTranslation(0, 100, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
    forth.layer.transform = transform;
    
    //5
    UIView *fifth = self.faces[4];
    transform = CATransform3DMakeTranslation(0, -100, 0);
    transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);
    fifth.layer.transform = transform;
    
    //6
    UIView *sixth = self.faces[5];
    transform = CATransform3DMakeTranslation(0, 0, -100);
    sixth.layer.transform = transform;
    
    
}

- (UIView *)contenterView {
    
    if (!_contenterView) {
        _contenterView = [[UIView alloc] initWithFrame:self.view.bounds];
        [self.view addSubview:_contenterView];
        _contenterView.backgroundColor = [UIColor lightGrayColor];
    }
    return _contenterView;
}

- (NSArray *)faces {
    
    if (!_faces) {
        
        NSMutableArray *faces = [NSMutableArray array];
        NSArray *colors = @[[UIColor redColor], [UIColor greenColor], [UIColor purpleColor], [UIColor orangeColor], [UIColor blueColor], [UIColor cyanColor]];
        
        for (int i = 0; i < 6; i++) {
            
            UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
            [self.contenterView addSubview:view];
            view.backgroundColor = colors[i];
            view.center = self.contenterView.center;
            
            [faces addObject:view];
            
        }
        _faces = faces;
    }
    return _faces;
}

相关文章
相关标签/搜索