iOS 動畫 第五章 變換

    //affineTransform
[self affineTransformTest];
    
    //affineTransforms
[self affineTransformsTest];
    
    //剪切變換
[self affineTransformShearTest];
    
    //transform3DTest
[self transform3DTest];
    
    //透視投影
[self transform3DM34Test];
    
    //sublayerTransform
[self sublayerTransformTest];
    
    //背面
[self doubleSidedTest];
    
    //扁平化圖層
[self outerInnerViewTest];
    
    //cubeTest
[self cubeTest];

仿射變換

//實際上UIView的transform屬性是一個CGAffineTransform類型,用于在二維空間做旋轉,縮放和平移。CGAffineTransform是一個可以和二維空間向量(例如CGPoint)做乘法的3X2的矩陣,圖層中平行的兩條線在變換之后任然保持平行
//創(chuàng)建一個CGAffineTransform
//CGAffineTransformMakeRotation(CGFloat angle)
//CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
//CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)

//UIView可以通過設置transform屬性做變換,但實際上它只是封裝了內部圖層的變換。
//CALayer同樣也有一個transform屬性,但它的類型是CATransform3D,而不是CGAffineTransform
//CALayer對應于UIView的transform屬性叫做affineTransform

- (void)affineTransformTest {
    layerView = [[UIView alloc] init];
    layerView.frame = CGRectMake(20.0, 20.0f, 100.0f, 150.0f);
    UIImage *image = [UIImage imageNamed:@"Meal"];
    layerView.layer.contents = (__bridge id)image.CGImage;
    [self.view addSubview:layerView];
    
    //rotate the layer 45 degrees
    CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_4);
    layerView.layer.affineTransform = transform;//layerView.transform = transform;
}

混合變換

//CGAffineTransformIdentity 創(chuàng)建一個CGAffineTransform類型的空值
//CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);

- (void)affineTransformsTest {
    layerView = [[UIView alloc] init];
    layerView.frame = CGRectMake(20.0, 20.0f, 100.0f, 150.0f);
    UIImage *image = [UIImage imageNamed:@"Meal"];
    layerView.layer.contents = (__bridge id)image.CGImage;
    [self.view addSubview:layerView];
    
    //create a new transform
    CGAffineTransform transform = CGAffineTransformIdentity;
    //scale by 50%
    transform = CGAffineTransformScale(transform, 0.5, 0.5);
    //rotate by 30 degrees
    transform = CGAffineTransformRotate(transform, M_PI/180.0f * 30.0f);
    // translate by 200 points
    transform = CGAffineTransformTranslate(transform, 200.0f, 0);
    //apply transform to layer
    layerView.layer.affineTransform = transform;
    //所以200像素的向右平移同樣也被旋轉了30度,縮小了50%,所以它實際上是斜向移動了100像素。
}

剪切變換

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

- (void)affineTransformShearTest {
    layerView = [[UIView alloc] init];
    layerView.frame = CGRectMake(20.0, 20.0f, 100.0f, 150.0f);
    UIImage *image = [UIImage imageNamed:@"Meal"];
    layerView.layer.contents = (__bridge id)image.CGImage;
    [self.view addSubview:layerView];
    //shear the layer at a 45_degree angle
    layerView.layer.affineTransform = CGAffineTransformMakeShear(1, 0);
}
//CG: Core Graphics    CA:Core Animation

3D 變換

//transform:(CATransform3D類型) zPosition
//CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
//CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)
//CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)

- (void)transform3DTest {
    layerView = [[UIView alloc] init];
    layerView.frame = CGRectMake(20.0, 20.0f, 100.0f, 150.0f);
    UIImage *image = [UIImage imageNamed:@"Meal"];
    layerView.layer.contents = (__bridge id)image.CGImage;
    [self.view addSubview:layerView];
    
    //rotate the layer 45 degree along the Y axis
    CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
    layerView.layer.transform = transform;
}

//透視投影 在3D變換中任然保持平行 在等距投影中,遠處的物體和近處的物體保持同樣的縮放比例,這種投影也有它自己的用處(例如建筑繪圖,顛倒,和偽3D視頻)
//投影變換(又稱作z變換) 控制:m34。m34用于按比例縮放X和Y的值來計算到底要離視角多遠。m34的默認值是0,我們可以通過設置m34為-1.0 / d來應用透視效果,d代表了想象中視角相機和屏幕之間的距離,以像素為單位, 通常500-1000就已經很好

- (void)transform3DM34Test {
    layerView = [[UIView alloc] init];
    layerView.frame = CGRectMake(20.0, 20.0f, 100.0f, 150.0f);
    UIImage *image = [UIImage imageNamed:@"Meal"];
    layerView.layer.contents = (__bridge id)image.CGImage;
    [self.view addSubview:layerView];
    
    //create a new transform
    CATransform3D transform = CATransform3DIdentity;
    //apply perspective
    transform.m34 = - 1.0/ 500.0f;
    //rotate by 45 degrees along the Y axis
    transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
    //apply to layer
    layerView.layer.transform = transform;
}

滅點

//Core Animation定義了這個點位于變換圖層的anchorPoint(通常位于圖層中心,但也有例外,見第三章)。這就是說,當圖層發(fā)生變換時,這個點永遠位于圖層變換之前anchorPoint的位置。
//當改變一個圖層的position,你也改變了它的滅點,做3D變換的時候要時刻記住這一點,當你視圖通過調整m34來讓它更加有3D效果,應該首先把它放置于屏幕中央,然后通過平移來把它移動到指定位置(而不是直接改變它的position),這樣所有的3D圖層都共享一個滅點。

sublayerTransform
//如果有多個視圖或者圖層,每個都做3D變換,那就需要分別設置相同的m34值,并且確保在變換之前都在屏幕中央共享同一個position
//CALayer有一個屬性叫做sublayerTransform。它也是CATransform3D類型,但和對一個圖層的變換不同,它影響到所有的子圖層。這意味著你可以一次性對包含這些圖層的容器做變換,于是所有的子圖層都自動繼承了這個變換方法。
//相較而言,通過在一個地方設置透視變換會很方便,同時它會帶來另一個顯著的優(yōu)勢:滅點被設置在容器圖層的中點,從而不需要再對子圖層分別設置了。這意味著你可以隨意使用position和frame來放置子圖層,而不需要把它們放置在屏幕中點,然后為了保證統(tǒng)一的滅點用變換來做平移

- (void)sublayerTransformTest {
    UIView *containerView = [[UIView alloc] init];
    containerView.frame = CGRectMake(20.f, 20.0f, 300.0f, 200.0f);
    containerView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:containerView];
    
    UIImageView *imageView1 = [[UIImageView alloc] init];
    imageView1.frame = CGRectMake(0.0, 50.0f, 100.0f, 100.0);
    imageView1.image = [UIImage imageNamed:@"Meal"];
    [containerView addSubview:imageView1];
    
    UIImageView *imageView2 = [[UIImageView alloc] init];
    imageView2.frame = CGRectMake(200.0, 50.0f, 100.0f, 100.0);
    imageView2.image = [UIImage imageNamed:@"Meal"];
    [containerView addSubview:imageView2];
    
    //apply perspective transform to container
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = - 1.0/500.0;
    containerView.layer.sublayerTransform = perspective;
    //rotate imageView1 by 45 degrees along thr Y axis
    CATransform3D transform1 = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
    imageView1.layer.transform = transform1;
    //rotate imageView2 by 45 degrees along thr Y axis
    CATransform3D transform2 = CATransform3DMakeRotation(-M_PI_4, 0, 1, 0);
    imageView2.layer.transform = transform2;
}

背面

//CALayer有一個叫做doubleSided的屬性來控制圖層的背面是否要被繪制。這是一個BOOL類型,默認為YES,如果設置為NO,那么當圖層正面從相機視角消失的時候,它將不會被繪制。

- (void)doubleSidedTest {
    UIImageView *imageView1 = [[UIImageView alloc] init];
    imageView1.frame = CGRectMake(5.0, 50.0f, 100.0f, 100.0);
    imageView1.image = [UIImage imageNamed:@"Meal"];
    [self.view addSubview:imageView1];
    
    //rotate imageView1 by 180 degrees along thr Y axis
    CATransform3D transform1 = CATransform3DMakeRotation(M_PI, 0, 1, 0);
    imageView1.layer.transform = transform1;
    imageView1.layer.doubleSided = YES;//鏡像圖片 默認為YES
}

扁平化圖層

- (void)outerInnerViewTest {
    UIView *outerView = [[UIView alloc] init];
    outerView.frame = CGRectMake(20.f, 20.0f, 300.0f, 300.0f);
    outerView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:outerView];
    
    UIImageView *innerView = [[UIImageView alloc] init];
    innerView.frame = CGRectMake(100.0, 100.0f, 100.0f, 100.0);
    innerView.image = [UIImage imageNamed:@"Meal"];
    [outerView addSubview:innerView];
    
    //rotate the outer layer 45 degrees
    //CATransform3D outer = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);
    CATransform3D outer = CATransform3DIdentity;
    outer.m34 = -1.0/500.0f;
    outer = CATransform3DRotate(outer, M_PI_4, 0, 1, 0);
    outerView.layer.transform = outer;
    //rotate the inner layer -45 degrees
   // CATransform3D inner = CATransform3DMakeRotation(-M_PI_4, 0, 0, 1);
    CATransform3D inner = CATransform3DIdentity;
    inner.m34 = -1.0/500.0f;
    inner = CATransform3DRotate(inner, -M_PI_4, 0, 1, 0);
    innerView.layer.transform = inner;
}

固體對象

- (void)addFace:(NSInteger )index withTransform:(CATransform3D)transform {
    //get the face view and add it to the container
    UIView *face = faces[index];
    [containerView addSubview:face];
    //center the face view within the container
    CGSize containerSize = containerView.bounds.size;
    face.center = CGPointMake(containerSize.width/2.f, containerSize.height/2.0f);
    //apply the transform
    face.layer.transform = transform;
    
    
    // apply lighting
    [self applyLightingToFace:face.layer];
}

- (void)cubeTest {
    containerView = [[UIView alloc] init];
    containerView.frame = CGRectMake(20.0f, 20.0f, 300.0f, 300.0f);
    containerView.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:containerView];
    
    faces = [NSMutableArray array];
    for (int k = 0; k < 6; k++) {
        UIView *face = [[UIView alloc] init];
        face.frame = CGRectMake(0, 0, 200.0f, 200.0f);
        face.backgroundColor = [UIColor whiteColor];
        
        UILabel *lab = [[UILabel alloc] init];
        lab.frame = CGRectMake(50.0, 50.0, 100.0f, 100.0f);
        lab.font = [UIFont systemFontOfSize:30];
        lab.text = [NSString stringWithFormat:@"%d", k + 1];
        lab.textAlignment = NSTextAlignmentCenter;
        
        if (k == 2) {
            UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
            button.frame = CGRectMake(50.0, 50.0, 100.0f, 100.0f);
            button.layer.cornerRadius = 5.0f;
            button.layer.borderWidth = 3.f;
            button.backgroundColor = [UIColor redColor];
            [button addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
            [face addSubview:button];
        }
        else {
            face.userInteractionEnabled = NO;
        }
        
        if ( k % 3 == 0) {
            lab.textColor = [UIColor redColor];
        }
        else if ( k % 3 == 1) {
            lab.textColor = [UIColor greenColor];
        }
        else if (k % 3 == 2) {
            lab.textColor = [UIColor blueColor];
        }
        [face addSubview:lab];
        [faces addObject:face];
    }
    
    //set up the container sublayer transform
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = -1.0/500.f;
    perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0);
    perspective = CATransform3DRotate(perspective, -M_PI_4, 0, 1, 0);
    containerView.layer.sublayerTransform = perspective;
    
    //add cube face 1
    CATransform3D trandform = CATransform3DMakeTranslation(0, 0, 100);
    [self addFace:0 withTransform:trandform];
    //add cude face2
    trandform = CATransform3DMakeTranslation(100, 0, 0);
    trandform = CATransform3DRotate(trandform, M_PI_2, 0, 1, 0);
    [self addFace:1 withTransform:trandform];
    //add cude face3
    trandform = CATransform3DMakeTranslation(0, -100, 0);
    trandform = CATransform3DRotate(trandform, M_PI_2, 1, 0, 0);
    [self addFace:2 withTransform:trandform];
    //add cude face4
    trandform = CATransform3DMakeTranslation(0, 100, 0);
    trandform = CATransform3DRotate(trandform, -M_PI_2, 1, 0, 0);
    [self addFace:3 withTransform:trandform];
    //add cude face5
    trandform = CATransform3DMakeTranslation(-100, 0, 0);
    trandform = CATransform3DRotate(trandform, -M_PI_2, 0, 1, 0);
    [self addFace:4 withTransform:trandform];
    //add cude face6
    trandform = CATransform3DMakeTranslation(0, 0, -100);
    trandform = CATransform3DRotate(trandform, M_PI, 0, 1, 0);
    [self addFace:5 withTransform:trandform];
}

光亮和陰影

- (void)applyLightingToFace:(CALayer *)face {
    //add lighting layer
    CALayer *layer = [CALayer layer];
    layer.frame = face.bounds;
    [face addSublayer:layer];
    //conver the face transform to matrix
    //(GLKMatrix4 has the same structure as CATransform3D)
    //譯者注:GLKMatrix4和CATransform3D內存結構一致,但坐標類型有長度區(qū)別,所以理論上應該做一次float到CGFloat的轉換,感謝[@zihuyishi](https://github.com/zihuyishi)同學~
    CATransform3D transform = face.transform;
    GLKMatrix4 matrix4 = *(GLKMatrix4 *)&transform;
    GLKMatrix3 matrix3 = GLKMatrix4GetMatrix3(matrix4);
    //get face normal
    GLKVector3 normal = GLKVector3Make(0, 0, 1);
    normal = GLKMatrix3MultiplyVector3(matrix3, normal);
    normal = GLKVector3Normalize(normal);
    //get dot product wiht light direction
    GLKVector3 light = GLKVector3Normalize(GLKVector3Make(LIGHT_DIRECTION));
    float dotProduct = GLKVector3DotProduct(light, normal);
    //set lighting layer opacity
    CGFloat shadow = 1 + dotProduct - AMBIENT_LIGHT;
    UIColor *color = [UIColor colorWithWhite:0 alpha:shadow];
    layer.backgroundColor = color.CGColor;
}

點擊事件

- (void)buttonAction {
    //按照視圖/圖層順序來說,4,5,6在3的前面
    //把除了表面3的其他視圖userInteractionEnabled屬性都設置成NO來禁止事件傳遞。或者簡單通過代碼把視圖3覆蓋在視圖6上。無論怎樣都可以點擊按鈕了
    NSLog(@"Three face is tapped!");
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容