我如何绕对角线行的CALayer? [英] How do i rotate a CALayer around a diagonal line?

查看:146
本文介绍了我如何绕对角线行的CALayer?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现一个翻转动画在棋盘游戏类似iPhone的应用程序中使用。动画应该看起来像一个游戏一块旋转和改变其背部的颜色(有点像一个黑白棋片)。我已经成功地创建翻转围绕其直交轴片的动画,但是当我试图通过改变绕Z轴的实际图像也将被旋转(这并不奇怪)旋转翻转它围绕一个对角线轴。相反,我想旋转图像原样围绕一个斜轴。

我曾试图改变 layer.sublayerTransform ,但没有成功。

下面是我目前的执行情况。它的工作原理做一招解决在动画的最后得到一个镜像图像的问题。该解决方案是不实际旋转层180度,而不是将其旋转90度,改变了图像,然后转动回来。

最终版本:基于Lorenzos建议,建立一个独立键控动画,并计算每帧的转换矩阵。这个版本而不是试图估算基于所述层尺寸需要指导的帧的号码,然后使用线性键控动画。该版本具有任意角度旋转,所以要绕对角线用45度角。

实例:

  [SomeClass的flipLayer:层图片:图片角度:M_PI / 4]

实施

   - (无效)animationDidStop:(CAAnimationGroup *)动画
                完成:(BOOL){完
  CALayer的*层= [动画valueForKey:@层];  如果([动画valueForKey:@名]的isEqual:@fadeAnimation]){
    / * code另一个动画* /
  }否则如果([动画valueForKey:@名]的isEqual:@flipAnimation]){
    layer.contents = [动画valueForKey:@形象];
  }  [层removeAllAnimations]
} - (无效)flipLayer:(CALayer的*)层
            图片:(CG​​ImageRef)图像
            角度:(浮点){角
  常量漂浮时间= 0.5F;  CAKeyframeAnimation *旋转= [CAKeyframeAnimation
                                 animationWithKeyPath:@改造];
  NSMutableArray里*值= [[[NSMutableArray里的alloc]初始化]自动释放];
  NSMutableArray里*倍= [[[NSMutableArray里的alloc]初始化]自动释放];
  / *更大的层需要更多的指导的价值观* /
  诠释帧= MAX(layer.bounds.size.width,layer.bounds.size.height)/ 2;
  INT I;
  对于(i = 0; I<帧;我++){
    / *创建一个比例值1.0将0.1〜1.0 * /
    浮规模= MAX(晶圆厂((浮点)(帧-I * 2)/(帧 - 1)),0.1);    CGAffineTransform T1,T2,T3;
    T1 = CGAffineTransformMakeRotation(角度);
    T2 = CGAffineTransformScale(T1,规模,1.0F);
    T3 = CGAffineTransformRotate(T2,利用广角);
    CATransform3D反= CATransform3DMakeAffineTransform(T3);    [值ADDOBJECT:[NSValue valueWithCATransform3D:反]];
    [次ADDOBJECT:[NSNumber的numberWithFloat:(浮点)I /(帧 - 1)]];
  }
  rotate.values​​ =值;
  rotate.keyTimes =倍;
  rotate.duration =持续时间;
  rotate.calculationMode = kCAAnimationLinear;  CAKeyframeAnimation *代替= [CAKeyframeAnimation
                                  animationWithKeyPath:@内容];
  replace.duration =时间/ 2;
  replace.beginTime =时间/ 2;
  replace.values​​ = [NSArray的arrayWithObjects:(id)的图像,无]
  replace.keyTimes = [NSArray的arrayWithObjects:
                      [NSNumber的numberWithDouble:0.0],无]
  replace.calculationMode = kCAAnimationDiscrete;  CAAnimationGroup *组= [CAAnimationGroup动画]
  group.duration =持续时间;
  group.timingFunction = [CAMediaTimingFunction
                          functionWithName:kCAMediaTimingFunctionLinear];
  group.animations = [NSArray的arrayWithObjects:旋转,请更换,零]
  group.delegate =自我;
  group.removedOnCompletion = NO;
  group.fillMode = kCAFillModeForwards;
  [组的setValue:@flipAnimationforKey:@名];
  [组的setValue:层forKey:@层];
  [组的setValue:(id)的图像forKey:@形象];  [层addAnimation:组forKey:无];
}

原来的code:

  +(无效)flipLayer:(CALayer的*)层
          toImage:(CGImageRef)图像
        withAngle:(双){角
  常量漂浮时间= 0.5F;  CAKeyframeAnimation *诊断= [CAKeyframeAnimation
                               animationWithKeyPath:@transform.rotation.z];
  diag.duration =持续时间;
  diag.values​​ = [NSArray的arrayWithObjects:
                 [NSNumber的numberWithDouble:角]
                 [NSNumber的numberWithDouble:0.0]
                 零];
  diag.keyTimes = [NSArray的arrayWithObjects:
                   [NSNumber的numberWithDouble:0.0]
                   [NSNumber的numberWithDouble:1.0F]
                   零];
  diag.calculationMode = kCAAnimationDiscrete;  CAKeyframeAnimation *翻转= [CAKeyframeAnimation
                               animationWithKeyPath:@transform.rotation.y];
  flip.duration =持续时间;
  flip.values​​ = [NSArray的arrayWithObjects:
                 [NSNumber的numberWithDouble:0.0]
                 [NSNumber的numberWithDouble:M_PI / 2],
                 [NSNumber的numberWithDouble:0.0]
                 零];
  flip.keyTimes = [NSArray的arrayWithObjects:
                   [NSNumber的numberWithDouble:0.0]
                   [NSNumber的numberWithDouble:0.5F]
                   [NSNumber的numberWithDouble:1.0F]
                   零];
  flip.calculationMode = kCAAnimationLinear;  CAKeyframeAnimation *代替= [CAKeyframeAnimation
                                  animationWithKeyPath:@内容];
  replace.duration =时间/ 2;
  replace.beginTime =时间/ 2;
  replace.values​​ = [NSArray的arrayWithObjects:(id)的图像,无]
  replace.keyTimes = [NSArray的arrayWithObjects:
                      [NSNumber的numberWithDouble:0.0],无]
  replace.calculationMode = kCAAnimationDiscrete;  CAAnimationGroup *组= [CAAnimationGroup动画]
  group.removedOnCompletion = NO;
  group.duration =持续时间;
  group.timingFunction = [CAMediaTimingFunction
                          functionWithName:kCAMediaTimingFunctionLinear];
  group.animations = [NSArray的arrayWithObjects:诊断,翻转,更换,零]
  group.fillMode = kCAFillModeForwards;  [层addAnimation:组forKey:无];
}


解决方案

您可以伪造它是这样的:创建一个仿射变换,它折叠层沿着它的对角线:

  A ----- B B
| | /
| | - >一个和D
| | /
ç----- D C

改变形象,并trasform的CALayer的回另一个动画。
这将创建一个围绕它的对角线旋转层的错觉。

如果我记得正确的数学为矩阵应该是:

  0.5 0.5 0
0.5 0.5 0
0 0 1

更新:
好吧,CA doen't真的喜欢用简并转换,但可以近似这种方式:

  CGAffineTransform T1 = CGAffineTransformMakeRotation(M_PI / 4.0F);
CGAffineTransform T2 = CGAffineTransformScale(T1,0.001f,1.0F);
CGAffineTransform T3 = CGAffineTransformRotate(T2,-M_PI / 4.0F);

在我的测试在模拟器上还有一个问题,因为发生了旋转比忒快译这样一个坚实的黑色方形的效果有点怪异。我想,如果你有与它周围的透明区域居中精灵效果会接近到什么期望。然后,您可以调整的 T3的值矩阵来看看你得到一个更有吸引力的结果。

更多的研究后,似乎应该动画它自己通过转型关键帧obtaim过渡本身的最大控制。说你是显示这个动画在第二个,你应该在使用kCAAnimationDiscrete第二withouot插每十分之一进行的10部矩阵;

:这些矩阵可以通过以下code生成

  CGAffineTransform T1 = CGAffineTransformMakeRotation(M_PI / 4.0F);
CGAffineTransform T2 = CGAffineTransformScale(T1,animationStepValue,1.0F);
CGAffineTransform T3 = CGAffineTransformRotate(T2,-M_PI / 4.0F);

在这里animationStepValue为关键帧的ECH从该进程采取:

  {1 0.7 0.5 0.3 0.1 0.3 0.5 0.7 1}

这是:您产生十个不同的变换矩阵(实际上9),推动它们作为对在第二的各第十显示关键帧,然后使用不插参数。你可以调整动画数量平衡平稳性和性能*

*对不起,可能出现的错误,最后一部分写没有拼写检查。

I'm trying to implement a flip animation to be used in board game like iPhone-application. The animation is supposed to look like a game piece that rotates and changes to the color of its back (kind of like an Reversi piece). I've managed to create an animation that flips the piece around its orthogonal axis, but when I try to flip it around a diagonal axis by changing the rotation around the z-axis the actual image also gets rotated (not surprisingly). Instead I would like to rotate the image "as is" around a diagonal axis.

I have tried to change layer.sublayerTransform but with no success.

Here is my current implementation. It works by doing a trick to resolve the issue of getting a mirrored image at the end of the animation. The solution is to not actually rotate the layer 180 degrees, instead it rotates it 90 degrees, changes image and then rotates it back.

Final version: Based on Lorenzos suggestion to create a discrete keyed animation and calculate the transformation matrix for each frame. This version instead tries to estimate number of "guiding" frames needed based on the layer size and then uses a linear keyed animation. This version rotates with a arbitrary angle so to rotate around diagonal line use a 45 degree angle.

Example usage:

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

Implementation:

- (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];
}

Original code:

+ (void)flipLayer:(CALayer *)layer
          toImage:(CGImageRef)image
        withAngle:(double)angle {
  const float duration = 0.5f;

  CAKeyframeAnimation *diag = [CAKeyframeAnimation
                               animationWithKeyPath:@"transform.rotation.z"];
  diag.duration = duration;
  diag.values = [NSArray arrayWithObjects:
                 [NSNumber numberWithDouble:angle],
                 [NSNumber numberWithDouble:0.0f],
                 nil];
  diag.keyTimes = [NSArray arrayWithObjects:
                   [NSNumber numberWithDouble:0.0f],
                   [NSNumber numberWithDouble:1.0f],
                   nil];
  diag.calculationMode = kCAAnimationDiscrete;

  CAKeyframeAnimation *flip = [CAKeyframeAnimation
                               animationWithKeyPath:@"transform.rotation.y"];
  flip.duration = duration;
  flip.values = [NSArray arrayWithObjects:
                 [NSNumber numberWithDouble:0.0f],
                 [NSNumber numberWithDouble:M_PI / 2],
                 [NSNumber numberWithDouble:0.0f],
                 nil];
  flip.keyTimes = [NSArray arrayWithObjects:
                   [NSNumber numberWithDouble:0.0f],
                   [NSNumber numberWithDouble:0.5f],
                   [NSNumber numberWithDouble:1.0f],
                   nil];
  flip.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.removedOnCompletion = NO;
  group.duration = duration;
  group.timingFunction = [CAMediaTimingFunction
                          functionWithName:kCAMediaTimingFunctionLinear];
  group.animations = [NSArray arrayWithObjects:diag, flip, replace, nil];
  group.fillMode = kCAFillModeForwards;

  [layer addAnimation:group forKey:nil];
}

解决方案

you can fake it this way: create an affine transform that collapse the layer along it's diagonal:

A-----B           B
|     |          /
|     |   ->   A&D
|     |        /
C-----D       C

change the image, and trasform the CALayer back in another animation. This will create the illusion of the layer rotating around its diagonal.

the matrix for that should be if I remember math correctly:

0.5 0.5 0
0.5 0.5 0
0   0   1

Update: ok, CA doen't really likes to use degenerate transforms, but you can approximate it this way:

CGAffineTransform t1 = CGAffineTransformMakeRotation(M_PI/4.0f);
CGAffineTransform t2 = CGAffineTransformScale(t1, 0.001f, 1.0f);
CGAffineTransform t3 = CGAffineTransformRotate(t2,-M_PI/4.0f);  

in my tests on the simulator there still was a problem because the rotations happens faster than te translation so with a solid black square the effect was a bit weird. I suppose that if you have a centered sprite with transparent area around it the effect will be close to what expected. You can then tweak the value of the t3 matrix to see if you get a more appealing result.

after more research, it appears that one should animate it's own transition via keyframes to obtaim the maximum control of the transition itself. say you were to display this animation in a second, you should make ten matrix to be shown at each tenth of a second withouot interpolation using kCAAnimationDiscrete; those matrix can be generated via the code below:

CGAffineTransform t1 = CGAffineTransformMakeRotation(M_PI/4.0f);
CGAffineTransform t2 = CGAffineTransformScale(t1, animationStepValue, 1.0f);
CGAffineTransform t3 = CGAffineTransformRotate(t2,-M_PI/4.0f);  

where animationStepValue for ech of the keyFrame is taken from this progression:

{1 0.7 0.5 0.3 0.1 0.3 0.5 0.7 1}

that is: you're generating ten different transformation matrix (actually 9), pushing them as keyframes to be shown at each tenth of a second, and then using the "don't interpolate" parameter. you can tweak the animation number for balancing smoothness and performance*

*sorry for possible errors, this last part was written without a spellchecker.

这篇关于我如何绕对角线行的CALayer?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆