Iphone 如何绕对角线旋转CALayer?

Iphone 如何绕对角线旋转CALayer?,iphone,animation,core-animation,calayer,flip,Iphone,Animation,Core Animation,Calayer,Flip,我正在尝试实现一个翻转动画,用于像iPhone应用程序这样的棋盘游戏。动画应该看起来像一个游戏块,旋转并改变其背部的颜色(有点像一个动画)。我已经成功地创建了一个动画,可以围绕其正交轴翻转工件,但是当我试图通过改变围绕z轴的旋转来围绕对角线轴翻转工件时,实际图像也会旋转(这并不奇怪)。相反,我想围绕一个对角轴旋转图像“原样” 我试图更改层。子层转换,但没有成功 这是我当前的实现。它的工作原理是使用一种技巧来解决在动画结束时获取镜像图像的问题。解决方案是不将图层实际旋转180度,而是将其旋转90度

我正在尝试实现一个翻转动画,用于像iPhone应用程序这样的棋盘游戏。动画应该看起来像一个游戏块,旋转并改变其背部的颜色(有点像一个动画)。我已经成功地创建了一个动画,可以围绕其正交轴翻转工件,但是当我试图通过改变围绕z轴的旋转来围绕对角线轴翻转工件时,实际图像也会旋转(这并不奇怪)。相反,我想围绕一个对角轴旋转图像“原样”

我试图更改
层。子层转换
,但没有成功

这是我当前的实现。它的工作原理是使用一种技巧来解决在动画结束时获取镜像图像的问题。解决方案是不将图层实际旋转180度,而是将其旋转90度,更改图像,然后将其旋转回去

最终版本:基于Lorenzos的建议,创建离散关键帧动画并计算每个帧的变换矩阵。此版本尝试根据层大小估计所需的“引导”帧数,然后使用线性关键帧动画。此版本以任意角度旋转,因此要围绕对角线旋转,请使用45度角

用法示例:

[someclass flipLayer:layer image:image angle:M_PI/4]
实施:

- (void)animationDidStop:(CAAnimationGroup *)animation
                finished:(BOOL)finished {
  CALayer *layer = [animation valueForKey:@"layer"];

  if([[animation valueForKey:@"name"] isEqual:@"fadeAnimation"]) {
    /* code for another animation */
  } else if([[animation valueForKey:@"name"] isEqual:@"flipAnimation"]) {
    layer.contents = [animation valueForKey:@"image"];
  }

  [layer removeAllAnimations];
}

- (void)flipLayer:(CALayer *)layer
            image:(CGImageRef)image
            angle:(float)angle {
  const float duration = 0.5f;

  CAKeyframeAnimation *rotate = [CAKeyframeAnimation
                                 animationWithKeyPath:@"transform"];
  NSMutableArray *values = [[[NSMutableArray alloc] init] autorelease];
  NSMutableArray *times = [[[NSMutableArray alloc] init] autorelease];
  /* bigger layers need more "guiding" values */
  int frames = MAX(layer.bounds.size.width, layer.bounds.size.height) / 2;
  int i;
  for (i = 0; i < frames; i++) {
    /* create a scale value going from 1.0 to 0.1 to 1.0 */
    float scale = MAX(fabs((float)(frames-i*2)/(frames - 1)), 0.1);

    CGAffineTransform t1, t2, t3;
    t1 = CGAffineTransformMakeRotation(angle);
    t2 = CGAffineTransformScale(t1, scale, 1.0f);
    t3 = CGAffineTransformRotate(t2, -angle);
    CATransform3D trans = CATransform3DMakeAffineTransform(t3);

    [values addObject:[NSValue valueWithCATransform3D:trans]];
    [times addObject:[NSNumber numberWithFloat:(float)i/(frames - 1)]];
  }
  rotate.values = values;
  rotate.keyTimes = times;
  rotate.duration = duration;
  rotate.calculationMode = kCAAnimationLinear;

  CAKeyframeAnimation *replace = [CAKeyframeAnimation
                                  animationWithKeyPath:@"contents"];
  replace.duration = duration / 2;
  replace.beginTime = duration / 2;
  replace.values = [NSArray arrayWithObjects:(id)image, nil];
  replace.keyTimes = [NSArray arrayWithObjects:
                      [NSNumber numberWithDouble:0.0f], nil];
  replace.calculationMode = kCAAnimationDiscrete;

  CAAnimationGroup *group = [CAAnimationGroup animation];
  group.duration = duration;
  group.timingFunction = [CAMediaTimingFunction
                          functionWithName:kCAMediaTimingFunctionLinear];
  group.animations = [NSArray arrayWithObjects:rotate, replace, nil];
  group.delegate = self;
  group.removedOnCompletion = NO;
  group.fillMode = kCAFillModeForwards;
  [group setValue:@"flipAnimation" forKey:@"name"];
  [group setValue:layer forKey:@"layer"];
  [group setValue:(id)image forKey:@"image"];

  [layer addAnimation:group forKey:nil];
}

您可以通过以下方式进行伪装:创建仿射变换,沿其对角线折叠层:

A-----B           B
|     |          /
|     |   ->   A&D
|     |        /
C-----D       C
更改图像,并在另一个动画中转换回CALayer。 这将产生层围绕其对角线旋转的错觉

如果我没记错数学,那么这个矩阵应该是:

0.5 0.5 0
0.5 0.5 0
0   0   1
更新: 好的,CA不太喜欢使用退化变换,但可以这样近似:

CGAffineTransform t1 = CGAffineTransformMakeRotation(M_PI/4.0f);
CGAffineTransform t2 = CGAffineTransformScale(t1, 0.001f, 1.0f);
CGAffineTransform t3 = CGAffineTransformRotate(t2,-M_PI/4.0f);  
在我在模拟器上的测试中仍然存在一个问题,因为旋转比te平移发生得快,所以对于实心黑色正方形,效果有点奇怪。我想如果你有一个中心的精灵,周围有一个透明的区域,效果将接近预期。然后可以调整t3矩阵的值,以查看是否得到更吸引人的结果

经过更多的研究,人们似乎应该通过关键帧为自己的过渡设置动画,以实现对过渡本身的最大控制。假设你要在一秒钟内显示这个动画,你应该制作十个矩阵,以每十分之一秒的速度显示,而不需要使用kCAAnimationDiscrete进行插值;这些矩阵可通过以下代码生成:

CGAffineTransform t1 = CGAffineTransformMakeRotation(M_PI/4.0f);
CGAffineTransform t2 = CGAffineTransformScale(t1, animationStepValue, 1.0f);
CGAffineTransform t3 = CGAffineTransformRotate(t2,-M_PI/4.0f);  
其中,关键帧ech的animationStepValue取自此进度:

{1 0.7 0.5 0.3 0.1 0.3 0.5 0.7 1}
也就是说:您将生成十个不同的变换矩阵(实际上是9),将它们作为关键帧,每十分之一秒显示一次,然后使用“don't interpolate”参数。可以调整动画编号以平衡平滑度和性能*


*对于可能出现的错误,很抱歉,最后一部分是在没有拼写检查的情况下编写的。

我解决了它。您可能已经有了一个解决方案,但以下是我的发现。这真的很简单

可以使用CABASICANIATION进行对角旋转,但它需要两个矩阵的串联,即层的现有矩阵加上CATTransformM3dRotate。“技巧”是,在3DRotate中,您需要指定要旋转的坐标

代码如下所示:


CATransform3DConcat(theLayer.transform, CATransform3DRotate(CATransform3DIdentity, M_PI/2, -1, 1, 0));

CABasicAnimation *ani1 = [CABasicAnimation animationWithKeyPath:@"transform"];

// set self as the delegate so you can implement (void)animationDidStop:finished: to handle anything you might want to do upon completion
[ani1 setDelegate:self];

// set the duration of the animation - a float
[ani1 setDuration:dur];

// set the animation's "toValue" which MUST be wrapped in an NSValue instance (except special cases such as colors)
ani1.toValue = [NSValue valueWithCATransform3D:CATransform3DConcat(theLayer.transform, CATransform3DRotate(CATransform3DIdentity, M_PI/2, -1, 1, 0))];

// give the animation a name so you can check it in the animationDidStop:finished: method
[ani1 setValue:@"shrink" forKey:@"name"];

// finally, apply the animation
[theLayer addAnimation:ani1 forKey@"arbitraryKey"];
这将使旋转看起来好像正方形的左上角围绕轴Y=X旋转,并移动到右下角

要设置动画的代码如下所示:


CATransform3DConcat(theLayer.transform, CATransform3DRotate(CATransform3DIdentity, M_PI/2, -1, 1, 0));

CABasicAnimation *ani1 = [CABasicAnimation animationWithKeyPath:@"transform"];

// set self as the delegate so you can implement (void)animationDidStop:finished: to handle anything you might want to do upon completion
[ani1 setDelegate:self];

// set the duration of the animation - a float
[ani1 setDuration:dur];

// set the animation's "toValue" which MUST be wrapped in an NSValue instance (except special cases such as colors)
ani1.toValue = [NSValue valueWithCATransform3D:CATransform3DConcat(theLayer.transform, CATransform3DRotate(CATransform3DIdentity, M_PI/2, -1, 1, 0))];

// give the animation a name so you can check it in the animationDidStop:finished: method
[ani1 setValue:@"shrink" forKey:@"name"];

// finally, apply the animation
[theLayer addAnimation:ani1 forKey@"arbitraryKey"];
就这样!该代码将旋转正方形(图层),使其在移动90度时不可见,并与屏幕垂直显示。然后,您可以更改颜色,并执行完全相同的动画使其恢复原状。同样的动画也可以工作,因为我们将矩阵串联在一起,所以每次要旋转时,只需执行两次,或者将
M_PI/2
更改为
M_PI

最后,应该注意的是,这让我抓狂,完成后,层将恢复到其原始状态,除非您显式地将其设置为结束动画状态。这意味着,就在第
[theLayer addAnimation:ani1 forKey@“arbitraykey”行之前您将要添加


theLayer.transform = CATransform3DConcat(v.theSquare.transform, CATransform3DRotate(CATransform3DIdentity, M_PI/2, -1, 1, 0));
在动画完成后设置其值。这将防止捕捉回原始状态

希望这有帮助。如果不是你,那么可能是其他人像我们一样把头撞在墙上!:)

干杯


Chris

下面是一个Xamarin iOS示例,我用它来拍打方形按钮的一角,就像狗耳朵(很容易移植到obj-c):

方法1:
x
y
轴使用带有
1
的旋转动画(Xamarin.iOS中的示例,但可轻松移植到obj-c):

方法2:只需将
x轴
旋转和
y轴
旋转添加到
CAanimation组
,即可同时运行:

// add to the UIView subclass you wish to rotate, where necessary
AnimateNotify(1.0, 0, UIViewAnimationOptions.CurveEaseOut | UIViewAnimationOptions.AllowUserInteraction | UIViewAnimationOptions.BeginFromCurrentState, () =>
{
    nfloat angleTo = -1 * (nfloat)Math.PI / 4;
    nfloat angleFrom = 0.0f ;
    string animKey = "rotate";

    // y-axis rotation
    var anim = new CABasicAnimation();
    anim.KeyPath = "transform.rotation.y";
    anim.AutoReverses = false;
    anim.Duration = 0.1f;
    anim.From = new NSNumber(angleFrom);
    anim.To = new NSNumber(angleTo);

    // x-axis rotation
    var animX = new CABasicAnimation();
    animX.KeyPath = "transform.rotation.x";
    animX.AutoReverses = false;
    animX.Duration = 0.1f;
    animX.From = new NSNumber(angleFrom);
    animX.To = new NSNumber(angleTo);

    // add both rotations to a group, to run simultaneously
    var animGroup = new CAAnimationGroup();
    animGroup.Duration = 0.1f;
    animGroup.AutoReverses = false;
    animGroup.Animations = new CAAnimation[] {anim, animX};
    Layer.AddAnimation(animGroup, animKey);

    // add perspective
    var transf = CATransform3D.Identity;
    transf.m34 = 1.0f / 500;
    Layer.Transform = transf;

}, null);

什么是对角轴?所谓对角轴,我的意思是旋转应该围绕层的对角线。比如,抓住图层的两个对角并旋转。比如,绕直线旋转
y=x
?谢谢你的回答。ASCII艺术是否应该显示剪切变换?如果我尝试从身份设置动画,然后到你的矩阵,然后再回到身份,我根本没有得到任何图像:(我查看了如何制作剪切矩阵,它是这样的:{{1,sheary,0},{shearx,1,0},{0,0,1}如果我在动画中使用它,它会接合一点,但我需要剪切很多,然后图像也会放大很多。没有剪切会形成平行四边形,这里我们尝试得到退化菱形,a&D角向中心移动,而不是a&B角