5

我第一次使用一些核心动画,在实现可以翻转的扑克牌的过程中,我决定使用 aCALayer来显示内容(不知道我将如何获得两个面,但这是另一个问题)我需要能够将其翻转、移动等...

我使用CATransaction成功了 - 在下面的片段中,卡片从左下角移动到左上角并翻转。问题是我不希望它向相反的方向翻转,但不知道如何告诉它,“嘿,你走错路了!”


[CATransaction begin]; 
[CATransaction setValue:[NSNumber numberWithFloat:2.0f] forKey:kCATransactionAnimationDuration];
myCard.position = CGPointMake(CGRectGetMidX(self.bounds)/2,CGRectGetMidY(self.bounds)/2);
myCard.transform = CATransform3DMakeRotation(M_PI, 1, 0, 0);
[CATransaction commit]; 

第二个问题是:我怎样才能让它同时进行两次转换?我试过嵌套两个CATransactions,但第二个只是覆盖第一个。我还尝试将旋转的矢量更改为 2D,比如说围绕 x 和 y 轴旋转,但这并不等同于仅通过 pi 围绕两个单独的轴翻转它。这是嵌套代码。


[CATransaction begin]; 
[CATransaction setValue:[NSNumber numberWithFloat:2.0f] forKey:kCATransactionAnimationDuration];
myCard.position = CGPointMake(CGRectGetMidX(self.bounds)/2,CGRectGetMidY(self.bounds)/2);
myCard.transform = CATransform3DMakeRotation(M_PI, 1, 0, 0); // rotate about x

  [CATransaction begin]; 
  [CATransaction setValue:[NSNumber numberWithFloat:1.0f] forKey:kCATransactionAnimationDuration];
  myCard.transform = CATransform3DMakeRotation(M_PI, 0, 1, 0); // rotate about y
  [CATransaction commit]; 

[CATransaction commit]; 

这里是UIView里面的动画块...我添加了角度滑块,x、y、z 用于旋转矢量,t 用于时间。平移发生在时间 = 2t 中,并且每次旋转应该只花费 t。

[CATransaction begin]; 
[CATransaction setValue:[NSNumber numberWithFloat:t * 2] forKey:kCATransactionAnimationDuration];
myCard.position = CGPointMake(CGRectGetMidX(self.view.bounds)/2,CGRectGetMidY(self.view.bounds)/2);

  [UIView beginAnimations:nil context:nil];
  [UIView setAnimationDuration:t];
  myCard.transform = CATransform3DMakeRotation(angle, x, y, z);
  [UIView commitAnimations]; 

  [UIView beginAnimations:nil context:nil];
  [UIView setAnimationDuration:t];
  [UIView setAnimationDelay:t];
  myCard.transform = CATransform3DMakeRotation(angle * 2, x, y, z);
  [UIView commitAnimations];

[CATransaction commit]; 

这就是我现在所处的位置:除了一个例外,这一切都有效。当卡片到达 pi/2 和 3*pi/2 时,y 旋转反转(开始向相反方向旋转)。它还在这些点处围绕 x 轴翻转。但是 x 和 z 工作得很好。很近!

CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,CGRectGetMidX(self.view.bounds)/2,CGRectGetMidY(self.view.bounds)/2*3);
CGPathAddCurveToPoint(thePath,NULL,
                      CGRectGetMidX(self.view.bounds)/2,CGRectGetMidY(self.view.bounds)/2*2,
                      CGRectGetMidX(self.view.bounds)/2,CGRectGetMidY(self.view.bounds)/2*1.5,
                      CGRectGetMidX(self.view.bounds)/2,CGRectGetMidY(self.view.bounds)/2);
CAKeyframeAnimation *moveAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];
moveAnimation.path=thePath;
CFRelease(thePath);

CABasicAnimation *xRotation;
xRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
xRotation.fromValue = [NSNumber numberWithFloat:0.0];
xRotation.toValue = [NSNumber numberWithFloat:x * angle * M_PI];

CABasicAnimation *yRotation;
yRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
yRotation.fromValue = [NSNumber numberWithFloat:0.0];
yRotation.toValue = [NSNumber numberWithFloat:y * angle * M_PI];

CABasicAnimation *zRotation;
zRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
zRotation.fromValue = [NSNumber numberWithFloat:0.0];
zRotation.toValue = [NSNumber numberWithFloat:z * angle * M_PI];

CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.duration = t;
groupAnimation.removedOnCompletion = NO;
groupAnimation.fillMode = kCAFillModeForwards;
groupAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
groupAnimation.animations = [NSArray arrayWithObjects:moveAnimation, xRotation, yRotation, zRotation, nil];

[myCard addAnimation:groupAnimation forKey:@"animateCard"];
4

3 回答 3

11

我相信在这种情况下您想要的是使用此答案中描述的辅助密钥路径:

CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI];
rotationAnimation.duration = duration;
[myCard.layer addAnimation:rotationAnimation forKey:@"rotationAnimation1"];

对于动画的第一阶段,它应该围绕 X 轴旋转。对于第二个,我相信如果你使用以下

CABasicAnimation *rotationAnimation2 = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
rotationAnimation2.toValue = [NSNumber numberWithFloat: M_PI];
rotationAnimation2.duration = duration2;
rotationAnimation2.cumulative = YES;
[myCard.layer addAnimation:rotationAnimation2 forKey:@"rotationAnimation2"];

它应该使动画累积并可能产生所需的效果。我自己没有尝试过,因此可能需要进行一些修改才能获得所需的确切结果。

当您直接使用变换时,Core Animation 会将变换从当前值插入到指定的变换。它将找到到达该变换的最短路径,这将限制动画方向。如果您尝试为同一个变换属性设置两次动画,则第二个值将简单地覆盖第一个值,而不是将两个变换组合在一起。

但是,当使用这样的辅助键路径时,Core Animation 将在您的起始角度和结束角度之间进行插值,因此您可以通过更改结束角度的符号来反转方向。它优化了角度值的变化,而不是底层变换的变化。您还应该能够在 keypath 上组合动画,因为在每个 keypath 操作组合的引擎盖下都会为您生成变换。

于 2010-09-27T15:42:21.633 回答
3

我猜测轴上的翻转是由 Apple 库内发生的万向节锁定引起的。99.999% 的时间都有效的简单解决方法是不使用精确的 0、90、180、270 和 360 度旋转。我已经在我的代码中看到过类似的情况,但我只是使用 (M_PI*0.99) 之类的东西而不是 M_PI。如果您使用带有欧拉角的视频卡矩阵乘法器,这总是一个问题。

要同时应用多个变换,您可以嵌套它们。这与 OpenGL 中的矩阵相乘完全一样。

// translate and scale at the same time
float scale = 1.6;
CATransform3D scaleMatrix = CATransform3DMakeScale(scale, scale, 1);
CATransform3D finalMatrix = CATransform3DTranslate(scaleMatrix, frame.size.width/scale, frame.size.height/scale, 0);
CABasicAnimation* animBottomTrans = [CABasicAnimation animationWithKeyPath:@"transform"];
[animBottomTrans setFromValue:[NSValue valueWithCATransform3D:finalMatrix]];
[animBottomTrans setToValue:[NSValue valueWithCATransform3D:CATransform3DIdentity]];
[animBottomTrans setDuration:duration];
[myLayer addAnimation:animBottomTrans forKey:@"transform"];

请记住,由于这些是嵌套的,因此您必须在其后的任何变换中处理前一个变换。在这段代码中,我必须更改我翻译的数量,因为我已经缩放了。如果您在 z 轴上旋转 90 度,然后在 y 轴上旋转 90 度,您实际上只是在 z 和 x 轴上旋转(z 旋转交换了您的 x 和 y 轴)。

于 2010-11-22T18:38:42.070 回答
2

The resulting transform is the same: (CGAffineTransform){-1,0,0,-1,0,0}. CoreAnimation picks the shortest rotation that will do the trick, and defaults to rotating clockwise (I think).

The easiest way to rotate the other way is to rotate by almost -M_PI (I'm not sure exactly how close you can get before it decides to rotate the "wrong" way).

The more complicated way is to break it up into two rotations: one from 0 to -M_PI/2 and one from -M_PI/2 to -M-PI. I think you can set an animation delay to make this happen...

于 2010-09-27T00:59:32.207 回答