【问题标题】:How to rotate a flat object around its center in perspective view?如何在透视图中围绕其中心旋转平面对象?
【发布时间】:2012-03-14 11:57:37
【问题描述】:

我正在尝试做的似乎应该很简单:我创建了一张旧留声机唱片的 2D 自上而下视图。我想在它的 X 轴上旋转它(把它放回去),然后围绕它的 Z 轴旋转它。

我在这里阅读了所有包含 CATransform3D 的问题,我阅读了 Steve Baker 的 "Matrices can be your friends" 文章以及 Bill Dudney 的书“Mac OS X 和 iPhone 的核心动画”,我认为 Brad Larson 的 @987654322 @ 有所有正确的代码,但由于他允许用户调整所有三个轴,我很难将他的代码缩小到我认为只是一维的(旋转的 z 轴)。

这是我正在测试的图像,并不是说细节对问题很重要:

我以通常的方式将其显示在屏幕上:(在 UIView 的子类中)

- (void)awakeFromNib
{
    UIImage *recordImage = [UIImage imageNamed:@"3DgoldRecord"];
    if (recordImage) {
        recordLayer = [CALayer layer];
        [recordLayer setFrame:CGRectMake(0.0, 0.0, 1024, 1024)];
        [recordLayer setContents:(id)[[UIImage imageNamed:@"3DgoldRecord"] CGImage]];
        [self.layer addSublayer:recordLayer];
    }
}

这是第一次测试,只是在屏幕上显示 ;-)

然后,为了“放回去”,我应用一个变换来绕图层的 X 轴旋转,在将图层的内容设置为图像之后和添加子图层之前插入此代码:

    CATransform3D myRotationTransform = 
                    CATransform3DRotate(recordLayer.transform,
                                        (M_PI_2 * 0.85), //experiment with flatness
                                        1.0, // rotate only across the x-axis
                                        0.0, // no y-axis transform
                                        0.0); //  no z-axis transform
    recordLayer.transform = myRotationTransform;        

效果如预期:记录很好。

对于下一步,导致记录旋转,我将此动画绑定到 touchesEnded 事件,尽管一旦退出测试/学习阶段,此旋转将不受用户控制:

CATransform3D currentTransform = recordLayer.transform; // to come back to at the end
CATransform3D myRotationTransform = 
                CATransform3DRotate(currentTransform,
                                    1.0, // go all the way around once
                                    (M_PI_2 * 0.85),    // x-axis change
                                    1.00,    // y-axis change ?
                                    0.0);   // z-axis change ?
recordLayer.transform = myRotationTransform;        
CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
myAnimation.duration = 5.0;
myAnimation.fromValue = [NSNumber numberWithFloat:0.0];
myAnimation.toValue = [NSNumber numberWithFloat:M_PI * 2.0];
myAnimation.delegate = self;
[recordLayer addAnimation:myAnimation forKey:@"transform.rotation"];

所以我很确定我挂断的是 CATransform3DRotate 调用中的向量(相信我:我一直在尝试对该向量进行简单更改以观察变化......现在列出的内容很简单我尝试的最后一次更改)。据我了解,transform中x、y、z的值,本质上是动画期间传入的值的百分比,范围从fromValue到toValue。

如果我在正确的轨道上理解这一点,是否可以在一次转换中表达这一点?或者我必须对每个有效的动画帧围绕 z 轴稍微旋转原始直立图像,然后通过 x 轴旋转放置结果?我看到一个关于组合变换的问题/答案,但那是一个比例变换,然后是一个旋转变换。我已经搞砸了变换变换,但没有做我认为我应该得到的事情(即将一个变换的结果传递给下一个变换参数似乎只是完全执行一个然后为另一个设置动画)。

【问题讨论】:

  • 我只是意识到我对 CABasicAnimation 的基本理解有点缺陷,尽管我认为这与我想要达到的结果无关。 CATransform3DRotate 调用创建的变换是 CABasicAnimation 结束时的最终结果。我认为它是一种函数,时间片被传递并用于计算时间片旋转。事实上,CABasicAnimation 正在插入实现最终结果所需的中间步骤。这意味着我不应该能够在我担心的一个变换/动画中做到这一点。

标签: ios catransform3d


【解决方案1】:

如果您使用CATransformLayer 作为图像视图层的父级,这是最简单的。因此,您需要一个使用CATransformLayer 作为其层的自定义视图子类。这是微不足道的:

@interface TransformView : UIView

@end

@implementation TransformView

+ (Class)layerClass {
    return [CATransformLayer class];
}

@end

TransformView 放入您的笔尖,并添加UIImageView 作为TransformView 的子视图。将这些视图连接到视图控制器中名为 transformViewdiscView 的插座。

在您的视图控制器中,设置transformView 的变换以应用透视(通过设置m34)和X 轴倾斜:

- (void)viewDidLoad
{
    [super viewDidLoad];
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = -1 / 500.0;
    transform = CATransform3DRotate(transform, .85 * M_PI_2, 1, 0, 0);
    self.transformView.layer.transform = transform;
}

viewWillAppear: 中为transform.rotation.zdiscView 的键路径添加动画,并在viewDidDisappear: 中将其删除:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    animation.fromValue = [NSNumber numberWithFloat:0];
    animation.toValue = [NSNumber numberWithFloat:2 * M_PI];
    animation.duration = 1.0;
    animation.repeatCount = HUGE_VALF;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    [self.discView.layer addAnimation:animation forKey:@"transform.rotation.z"];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.discView.layer removeAllAnimations];
}

结果:

更新

这是一个 Swift 操场演示:

import UIKit
import XCPlayground

class TransformView: UIView {
    override class func layerClass() -> AnyClass {
        return CATransformLayer.self
    }
}

let view = UIView(frame: CGRectMake(0, 0, 300, 150))
view.backgroundColor = UIColor.groupTableViewBackgroundColor()
XCPlaygroundPage.currentPage.liveView = view

let transformView = TransformView(frame: view.bounds)
view.addSubview(transformView)

var transform = CATransform3DIdentity
transform.m34 = CGFloat(-1) / transformView.bounds.width
transform = CATransform3DRotate(transform, 0.85 * CGFloat(M_PI_2), 1, 0, 0)
transformView.layer.transform = transform

let image = UIImage(named: "3DgoldRecord")!
let imageView = UIImageView(image: image)
imageView.bounds = CGRectMake(0, 0, 200, 200)
imageView.center = CGPointMake(transformView.bounds.midX, transformView.bounds.midY)
transformView.addSubview(imageView)

let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.fromValue = 0
animation.toValue = 2 * M_PI
animation.duration = 1
animation.repeatCount = Float.infinity
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
imageView.layer.addAnimation(animation, forKey: animation.keyPath)

将问题中的图片复制到 Playground Resources 文件夹中,并将其命名为 3DgoldRecord.png。结果:

【讨论】:

  • 罗伯,非常感谢。我已经上床睡觉了,但就像经常发生的那样,大脑不会关闭。我回到我的电脑前尝试改变 tommyli 的建议,但看到了你的旋转记录.gif!你的回答很清楚。我之前没有看到关于如何将视图的图层变为 CATransformLayer 的描述;可能是由于搜索 CATransform3D。尽管如此,在我的书中,您至少向我展示了如何正确使用它而受到赞誉!
  • 发痒! :-) 我必须像其他地方提到的那样设置 zPosition,这样光盘在旋转到 -z 空间时不会消失在我的背景后面,并增加了透视除数,但现在看起来很棒。谢谢!!!
  • @Rob 你能把它转换成 OpenGL 吗?实际上我需要对 CATransform3D 图层进行屏幕截图。但是没有opengl它就无法工作。请提出建议。
  • 我对使用 OpenGL 重写它不感兴趣。
  • 测试项目链接消失。你能给我发一个演示吗?非常感谢
【解决方案2】:

你可以考虑用一个超级层包裹recordLayer。将 x 轴旋转应用到 superlayer 并将 z 轴旋转动画添加到 recordLayer。

【讨论】:

  • 谢谢,tommyli,如果我实施的方法能按照我的预期进行,那就很接近了。我用 y 轴代替了您对 z 轴的推荐,因为超层没有被转换。我期待“旋转镍”效应,“凸起”的背部会旋转并在前面(然后继续绕到后面)。但无论如何,我真正看到的只是围绕 z 轴旋转。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-06
  • 1970-01-01
  • 2016-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多