ios 如何使 CATransform3dMakeRotation 以另一种方式旋转?而链在一起
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3799194/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make a CATransform3dMakeRotation rotate the other way? And chain together
提问by Steve
I'm working with some Core Animation for the first time and in the process of implementing a playing card that I can flip around, I've decided to use a CALayer
to display the contents (not sure how I'm going to get two sides, but that's another question) and I need to be able to flip it over, move it, etc...
我第一次使用一些核心动画,在实现一张我可以翻转的扑克牌的过程中,我决定使用 aCALayer
来显示内容(不确定我将如何得到两个方面,但这是另一个问题)我需要能够翻转它,移动它等等......
I'm using CATransaction
with some success - in the snippet below, the card moves from the bottom left to the top left and flips over. The problem is that I wan't it to flip the opposite way, but don't know how to tell it, "hey, you're going the wrong way!"
我的使用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];
A second question would be: how can I get it to do two transforms at once? I've tried nesting two CATransactions
, but the second one just overrides the first. I also tried changing the vector for the rotation to be 2D, like saying to rotate around x and y axes, but that isn't equivalent to just flipping it by pi around two individual axes. Here's the nested code.
第二个问题是:我怎样才能让它一次做两个变换?我试过嵌套两个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];
And here it is with UIView
animation blocks inside... I've added sliders for the angle, x, y, z for the rotation vector, and t for time. The translation takes place over time = 2t and each of the rotations should take just t each.
这是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];
And this is where I am now: It all works with one exception. The y rotation reverses (starts rotating in the opposite direction) when the card gets to pi/2 and 3*pi/2. It also flips about the x axis at these points. But x and z work great. So close!
这就是我现在所处的位置:除了一个例外,这一切都有效。当卡达到 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"];
回答by Brad Larson
I believe that what you want in this case is to use a helper keypath like is described in this answer:
我相信在这种情况下您想要的是使用此答案中描述的辅助键路径:
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI];
rotationAnimation.duration = duration;
[myCard.layer addAnimation:rotationAnimation forKey:@"rotationAnimation1"];
which should rotate about the X axis for the first stage of your animation. For the second, I believe if you use the following
在动画的第一阶段,它应该围绕 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"];
it should make the animations cumulative and may produce the desired effect. I haven't tried this myself, so some tinkering may be required to get the exact result you need.
它应该使动画累积并可能产生所需的效果。我自己还没有尝试过,因此可能需要进行一些修补才能获得所需的确切结果。
When you are working with a transform directly, Core Animation will interpolate the transform from the current value to the specified transform. It will find the shortest path to get to that transform, which will restrict the animation direction. If you try to animate the same transform property twice, the second value will simply override the first, not combine the two transforms together.
当您直接使用变换时,Core Animation 会将变换从当前值插入到指定的变换。它将找到到达该变换的最短路径,这将限制动画方向。如果您尝试对同一个变换属性进行两次动画处理,则第二个值将简单地覆盖第一个值,而不是将两个变换组合在一起。
However, when using a helper keypath like this, Core Animation will interpolate between your starting angle and ending angle, so you can reverse direction by changing the sign of the ending angle. It optimizes the change in angle value, not the change in the underlying transform. You also should be able to combine animations on the keypath, because the transform is generated for you under the hood for each combination of keypath manipulations.
但是,当使用这样的辅助键路径时,Core Animation 将在您的开始角度和结束角度之间进行插值,因此您可以通过更改结束角度的符号来反转方向。它优化了角度值的变化,而不是底层变换的变化。您还应该能够在关键路径上组合动画,因为对于每个关键路径操作组合,都会在幕后为您生成变换。
回答by Gabe
I'm guessing the flipping on an axis is caused by gimbal lock happening inside Apple's library. The easy fix that works 99.999% of the time is to just not use exact 0, 90, 180, 270, and 360 degree rotations. I've seen stuff like this happen in my code, but I just use something like (M_PI*0.99) instead of M_PI. This is always a problem if you are using the video card's matrix multipliers with euler angles.
我猜轴上的翻转是由 Apple 库内发生的万向节锁定引起的。在 99.999% 的时间里有效的简单解决方法是不使用精确的 0、90、180、270 和 360 度旋转。我在我的代码中看到过这样的事情发生,但我只是使用类似 (M_PI*0.99) 的东西而不是 M_PI。如果您使用具有欧拉角的视频卡的矩阵乘法器,这总是一个问题。
To apply multiple transforms at the same time, you can nest them. This works exactly like multiplying matrices in OpenGL.
要同时应用多个变换,您可以嵌套它们。这与 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"];
Remember that since these are nested you have to deal with the previous transform in any transforms that come after it. In this code I have to change the amount I translated because I have already scaled. If you rotate 90degrees on the z axis and then rotate 90 degrees on the y axis, you have effectively just rotated on the z and x axises (the z rotation swaps your x and y axises).
请记住,由于这些是嵌套的,因此您必须在它之后的任何转换中处理之前的转换。在这段代码中,我必须更改翻译的数量,因为我已经缩放了。如果您在 z 轴上旋转 90 度,然后在 y 轴上旋转 90 度,那么您实际上只是在 z 轴和 x 轴上旋转了(z 旋转交换了您的 x 轴和 y 轴)。
回答by tc.
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).
产生的变换是相同的:(CGAffineTransform){-1,0,0,-1,0,0}
。CoreAnimation 选择最短的旋转来完成任务,并且默认为顺时针旋转(我认为)。
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).
以另一种方式旋转的最简单方法是几乎旋转-M_PI
(在它决定以“错误”方式旋转之前,我不确定您能获得多近的距离)。
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...
更复杂的方法是将其分成两次旋转:一次从 0 到 -M_PI/2,另一次从 -M_PI/2 到 -M-PI。我认为您可以设置动画延迟来实现这一点......