2011-03-28 107 views
9

我試圖在兩個UIView上只使用圖層功能來實現一種摺紙轉換。這個想法是用透視效果來摺疊兩個視圖。兩種觀點都有一個觀點,過渡是通過對每個觀點的旋轉來定義的,以及對一個觀點的譯文,使得這個觀點似乎被附加到另一個觀點上。使用CATransform3D透視的摺紙轉換

問題是視圖在轉換過程中彼此重疊。我不想使用zPosition來在視覺上避免這種重疊,我真的希望這兩個視圖的行爲就好像它們被共享方綁定在一起。這是轉換的代碼。

任何想法,或任何其他解決方案?

Overlapping views during transition

- (void)animateWithPerspective 
{ 
    CGFloat rotationAngle = 90; 
    CATransform3D transform = CATransform3DIdentity; 
    UIView *topView; 
    UIView *bottomView; 
    UIView *mainView; 
    CGRect frame; 
    CGFloat size = 200; 

    mainView = [[UIView alloc] initWithFrame:CGRectMake(10,10, size, size*2)]; 
    [self.view addSubview:mainView]; 
    bottomView = [[UIView alloc] initWithFrame:CGRectZero]; 
    bottomView.layer.anchorPoint = CGPointMake(0.5, 1); 
    bottomView.frame = CGRectMake(0, size, size, size); 
    bottomView.backgroundColor = [UIColor blueColor]; 
    [mainView addSubview:bottomView]; 

    topView = [[UIView alloc] initWithFrame:CGRectZero]; 
    topView.layer.anchorPoint = CGPointMake(0.5, 0); 
    topView.frame = CGRectMake(0, 0, size, size); 
    topView.backgroundColor = [UIColor redColor]; 
    [mainView addSubview:topView]; 

    transform.m34 = 1.0/700.0; 
    topView.layer.transform = transform; 
    bottomView.layer.transform = transform; 

    [UIView beginAnimations:nil context:nil]; 
    [UIView setAnimationDuration:2]; 
    [UIView setAnimationRepeatAutoreverses:YES]; 
    [UIView setAnimationRepeatCount:INFINITY]; 
    [UIView setAnimationCurve:UIViewAnimationCurveLinear]; 
    frame = bottomView.frame; 
    frame.origin.y = bottomView.frame.origin.y - bottomView.frame.size.height - topView.frame.size.height; 
    bottomView.frame = frame; 
    topView.layer.transform = CATransform3DRotate(transform, rotationAngle * M_PI/180, 1, 0, 0); 
    bottomView.layer.transform = CATransform3DRotate(transform, -rotationAngle * M_PI/180, 1, 0, 0); 
    [UIView commitAnimations]; 
} 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    [self animate]; 
} 

爲了簡化問題,讓我們擺脫任何角度變換。下面是一個簡單的代碼用同一種問題:

- (void)animateWithoutPerspective 
{ 
    CGFloat rotationAngle = 90; 
    UIView *topView; 
    UIView *bottomView; 
    UIView *mainView; 
    CGRect frame; 
    CGFloat size = 200; 

    mainView = [[UIView alloc] initWithFrame:CGRectMake(10,10, size, size*2)]; 
    [self.view addSubview:mainView]; 
    bottomView = [[UIView alloc] initWithFrame:CGRectMake(0, size, size, size)]; 
    bottomView.backgroundColor = [UIColor blueColor]; 
    [mainView addSubview:bottomView]; 

    topView = [[UIView alloc] initWithFrame:CGRectZero]; 
    topView.layer.anchorPoint = CGPointMake(0.5, 0); 
    topView.frame = CGRectMake(10, 0, size-20, size); 
    topView.backgroundColor = [UIColor redColor]; 
    [mainView addSubview:topView]; 

    [UIView beginAnimations:nil context:nil]; 
    [UIView setAnimationDuration:2]; 
    [UIView setAnimationRepeatAutoreverses:YES]; 
    [UIView setAnimationRepeatCount:INFINITY]; 
    [UIView setAnimationCurve:UIViewAnimationCurveLinear]; 
    frame = bottomView.frame; 
    frame.origin.y = bottomView.frame.origin.y - bottomView.frame.size.height; 
    bottomView.frame = frame; 
    topView.layer.transform = CATransform3DMakeRotation(rotationAngle * M_PI/180, 1, 0, 0); 
    [UIView commitAnimations]; 
} 

回答

21

最後,這裏是添加了簡單陰影的三袖動畫的一些解決方案。解決這種動畫的關鍵是要使用幾個組織良好的子層,還有一些CATransformLayer

- (void)animate 
{ 
    CATransform3D transform = CATransform3DIdentity; 
    CALayer *topSleeve; 
    CALayer *middleSleeve; 
    CALayer *bottomSleeve; 
    CALayer *topShadow; 
    CALayer *middleShadow; 
    UIView *mainView; 
    CGFloat width = 300; 
    CGFloat height = 150; 
    CALayer *firstJointLayer; 
    CALayer *secondJointLayer; 
    CALayer *perspectiveLayer; 

    mainView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, width, height*3)]; 
    mainView.backgroundColor = [UIColor yellowColor]; 
    [self.view addSubview:mainView]; 

    perspectiveLayer = [CALayer layer]; 
    perspectiveLayer.frame = CGRectMake(0, 0, width, height*2); 
    [mainView.layer addSublayer:perspectiveLayer]; 

    firstJointLayer = [CATransformLayer layer]; 
    firstJointLayer.frame = mainView.bounds; 
    [perspectiveLayer addSublayer:firstJointLayer]; 

    topSleeve = [CALayer layer]; 
    topSleeve.frame = CGRectMake(0, 0, width, height); 
    topSleeve.anchorPoint = CGPointMake(0.5, 0); 
    topSleeve.backgroundColor = [UIColor redColor].CGColor; 
    topSleeve.position = CGPointMake(width/2, 0); 
    [firstJointLayer addSublayer:topSleeve]; 
    topSleeve.masksToBounds = YES; 

    secondJointLayer = [CATransformLayer layer]; 
    secondJointLayer.frame = mainView.bounds; 
    secondJointLayer.frame = CGRectMake(0, 0, width, height*2); 
    secondJointLayer.anchorPoint = CGPointMake(0.5, 0); 
    secondJointLayer.position = CGPointMake(width/2, height); 
    [firstJointLayer addSublayer:secondJointLayer]; 

    middleSleeve = [CALayer layer]; 
    middleSleeve.frame = CGRectMake(0, 0, width, height); 
    middleSleeve.anchorPoint = CGPointMake(0.5, 0); 
    middleSleeve.backgroundColor = [UIColor blueColor].CGColor; 
    middleSleeve.position = CGPointMake(width/2, 0); 
    [secondJointLayer addSublayer:middleSleeve]; 
    middleSleeve.masksToBounds = YES; 

    bottomSleeve = [CALayer layer]; 
    bottomSleeve.frame = CGRectMake(0, height, width, height); 
    bottomSleeve.anchorPoint = CGPointMake(0.5, 0); 
    bottomSleeve.backgroundColor = [UIColor grayColor].CGColor; 
    bottomSleeve.position = CGPointMake(width/2, height); 
    [secondJointLayer addSublayer:bottomSleeve]; 

    firstJointLayer.anchorPoint = CGPointMake(0.5, 0); 
    firstJointLayer.position = CGPointMake(width/2, 0); 

    topShadow = [CALayer layer]; 
    [topSleeve addSublayer:topShadow]; 
    topShadow.frame = topSleeve.bounds; 
    topShadow.backgroundColor = [UIColor blackColor].CGColor; 
    topShadow.opacity = 0; 

    middleShadow = [CALayer layer]; 
    [middleSleeve addSublayer:middleShadow]; 
    middleShadow.frame = middleSleeve.bounds; 
    middleShadow.backgroundColor = [UIColor blackColor].CGColor; 
    middleShadow.opacity = 0; 

    transform.m34 = -1.0/700.0; 
    perspectiveLayer.sublayerTransform = transform; 

    CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"]; 
    [animation setDuration:2]; 
    [animation setAutoreverses:YES]; 
    [animation setRepeatCount:INFINITY]; 
    [animation setFromValue:[NSNumber numberWithDouble:0]]; 
    [animation setToValue:[NSNumber numberWithDouble:-90*M_PI/180]]; 
    [firstJointLayer addAnimation:animation forKey:nil]; 

    animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"]; 
    [animation setDuration:2]; 
    [animation setAutoreverses:YES]; 
    [animation setRepeatCount:INFINITY]; 
    [animation setFromValue:[NSNumber numberWithDouble:0]]; 
    [animation setToValue:[NSNumber numberWithDouble:180*M_PI/180]]; 
    [secondJointLayer addAnimation:animation forKey:nil]; 

    animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"]; 
    [animation setDuration:2]; 
    [animation setAutoreverses:YES]; 
    [animation setRepeatCount:INFINITY]; 
    [animation setFromValue:[NSNumber numberWithDouble:0]]; 
    [animation setToValue:[NSNumber numberWithDouble:-90*M_PI/180]]; 
    [bottomSleeve addAnimation:animation forKey:nil]; 

    animation = [CABasicAnimation animationWithKeyPath:@"bounds.size.height"]; 
    [animation setDuration:2]; 
    [animation setAutoreverses:YES]; 
    [animation setRepeatCount:INFINITY]; 
    [animation setFromValue:[NSNumber numberWithDouble:perspectiveLayer.bounds.size.height]]; 
    [animation setToValue:[NSNumber numberWithDouble:0]]; 
    [perspectiveLayer addAnimation:animation forKey:nil]; 

    animation = [CABasicAnimation animationWithKeyPath:@"position.y"]; 
    [animation setDuration:2]; 
    [animation setAutoreverses:YES]; 
    [animation setRepeatCount:INFINITY]; 
    [animation setFromValue:[NSNumber numberWithDouble:perspectiveLayer.position.y]]; 
    [animation setToValue:[NSNumber numberWithDouble:0]]; 
    [perspectiveLayer addAnimation:animation forKey:nil]; 

    animation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 
    [animation setDuration:2]; 
    [animation setAutoreverses:YES]; 
    [animation setRepeatCount:INFINITY]; 
    [animation setFromValue:[NSNumber numberWithDouble:0]]; 
    [animation setToValue:[NSNumber numberWithDouble:0.5]]; 
    [topShadow addAnimation:animation forKey:nil]; 

    animation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 
    [animation setDuration:2]; 
    [animation setAutoreverses:YES]; 
    [animation setRepeatCount:INFINITY]; 
    [animation setFromValue:[NSNumber numberWithDouble:0]]; 
    [animation setToValue:[NSNumber numberWithDouble:0.5]]; 
    [middleShadow addAnimation:animation forKey:nil]; 
} 
+0

+1精彩的東西! – haroldcampbell 2011-10-31 02:46:20

+0

非常感謝您將CATransformLayer放在我的雷達上。在咬牙切齒之後,我終於得到了結果。 – fzwo 2012-02-12 21:10:38

+0

http://stackoverflow.com/questions/13559243/vertical-fold-animation ..當我嘗試相同的垂直我越來越反轉視圖動畫..我已經發布鏈接..你能給一些幫助.. – 2012-11-26 06:39:38

1

起初我還以爲是Y位置的線性變換不會意味着旋轉的線性變換,但它似乎是這樣。

該錯誤非常簡單,透視值錯誤,透視圖是通過在Z軸上定位一個負距離的天文臺來建模的。那麼您需要取消透視值:

transform.m34 = 1.0/(-700.0); 

而且它確實按預期工作。

只是爲了記錄,角度的轉換不是線性的。但是神器被zbuffer隱藏了。

在中間路徑的角度將是60度,但與線性動畫我們得到45度。但從右側看,從負Z軸位置看,緩衝區隱藏了平面交叉點。

3

菲爾的回答

func animate() { 
    var transform:CATransform3D = CATransform3DIdentity; 
    var topSleeve:CALayer 
    var middleSleeve:CALayer 
    var bottomSleeve:CALayer 
    var topShadow:CALayer 
    var middleShadow:CALayer 
    var mainView:UIView 
    var width:CGFloat = 300 
    var height:CGFloat = 150 
    var firstJointLayer:CALayer 
    var secondJointLayer:CALayer 
    var perspectiveLayer:CALayer 

    mainView = UIView(frame:CGRectMake(50, 50, width, height*3)) 
    mainView.backgroundColor = UIColor.yellowColor() 
    view.addSubview(mainView) 

    perspectiveLayer = CALayer() 
    perspectiveLayer.frame = CGRectMake(0, 0, width, height*2) 
    mainView.layer.addSublayer(perspectiveLayer) 

    firstJointLayer = CATransformLayer() 
    firstJointLayer.frame = mainView.bounds; 
    perspectiveLayer.addSublayer(firstJointLayer) 

    topSleeve = CALayer() 
    topSleeve.frame = CGRectMake(0, 0, width, height); 
    topSleeve.anchorPoint = CGPointMake(0.5, 0) 
    topSleeve.backgroundColor = UIColor.redColor().CGColor; 
    topSleeve.position = CGPointMake(width/2, 0) 
    firstJointLayer.addSublayer(topSleeve) 
    topSleeve.masksToBounds = true 

    secondJointLayer = CATransformLayer() 
    secondJointLayer.frame = mainView.bounds; 
    secondJointLayer.frame = CGRectMake(0, 0, width, height*2) 
    secondJointLayer.anchorPoint = CGPointMake(0.5, 0) 
    secondJointLayer.position = CGPointMake(width/2, height) 
    firstJointLayer.addSublayer(secondJointLayer) 

    middleSleeve = CALayer() 
    middleSleeve.frame = CGRectMake(0, 0, width, height); 
    middleSleeve.anchorPoint = CGPointMake(0.5, 0) 
    middleSleeve.backgroundColor = UIColor.blueColor().CGColor 
    middleSleeve.position = CGPointMake(width/2, 0) 
    secondJointLayer.addSublayer(middleSleeve) 
    middleSleeve.masksToBounds = true 

    bottomSleeve = CALayer() 
    bottomSleeve.frame = CGRectMake(0, height, width, height) 
    bottomSleeve.anchorPoint = CGPointMake(0.5, 0) 
    bottomSleeve.backgroundColor = UIColor.grayColor().CGColor 
    bottomSleeve.position = CGPointMake(width/2, height) 
    secondJointLayer.addSublayer(bottomSleeve) 

    firstJointLayer.anchorPoint = CGPointMake(0.5, 0) 
    firstJointLayer.position = CGPointMake(width/2, 0) 

    topShadow = CALayer() 
    topSleeve.addSublayer(topShadow) 
    topShadow.frame = topSleeve.bounds 
    topShadow.backgroundColor = UIColor.blackColor().CGColor 
    topShadow.opacity = 0 

    middleShadow = CALayer() 
    middleSleeve.addSublayer(middleShadow) 
    middleShadow.frame = middleSleeve.bounds 
    middleShadow.backgroundColor = UIColor.blackColor().CGColor 
    middleShadow.opacity = 0 

    transform.m34 = -1/700 
    perspectiveLayer.sublayerTransform = transform; 

    var animation = CABasicAnimation(keyPath: "transform.rotation.x") 
    animation.duration = 2 
    animation.autoreverses = true 
    animation.repeatCount = 1000 
    animation.fromValue = 0 
    animation.toValue = -90*M_PI/180 
    firstJointLayer.addAnimation(animation, forKey: nil) 

    animation = CABasicAnimation(keyPath: "transform.rotation.x") 
    animation.duration = 2 
    animation.autoreverses = true 
    animation.repeatCount = 1000 
    animation.fromValue = 0 
    animation.toValue = 180*M_PI/180 
    secondJointLayer.addAnimation(animation, forKey: nil) 

    animation = CABasicAnimation(keyPath: "transform.rotation.x") 
    animation.duration = 2 
    animation.autoreverses = true 
    animation.repeatCount = 1000 
    animation.fromValue = 0 
    animation.toValue = -90*M_PI/180 
    bottomSleeve.addAnimation(animation, forKey: nil) 

    animation = CABasicAnimation(keyPath: "bounds.size.height") 
    animation.duration = 2 
    animation.autoreverses = true 
    animation.repeatCount = 1000 
    animation.fromValue = perspectiveLayer.bounds.size.height 
    animation.toValue = 0 
    perspectiveLayer.addAnimation(animation, forKey: nil) 


    animation = CABasicAnimation(keyPath: "position.y") 
    animation.duration = 2 
    animation.autoreverses = true 
    animation.repeatCount = 1000 
    animation.fromValue = perspectiveLayer.position.y 
    animation.toValue = 0 
    perspectiveLayer.addAnimation(animation, forKey: nil) 

    animation = CABasicAnimation(keyPath: "opacity") 
    animation.duration = 2 
    animation.autoreverses = true 
    animation.repeatCount = 1000 
    animation.fromValue = 0 
    animation.toValue = 0.5 
    topShadow.addAnimation(animation, forKey: nil) 

    animation = CABasicAnimation(keyPath: "opacity") 
    animation.duration = 2 
    animation.autoreverses = true 
    animation.repeatCount = 1000 
    animation.fromValue = 0 
    animation.toValue = 0.5 
    middleShadow.addAnimation(animation, forKey: nil) 
} 
0

的斯威夫特版本爲了說明問題的答案。

我沒有把所有的動畫和透視投影(perspectiveLayer.sublayerTransformCATransformLayer subLayers)。使用投影矩陣m34字段值來查看它如何影響消失點。

Stack of layers