【问题标题】:Failed Attempt to use Perspective Transformations in Core Animation尝试在核心动画中使用透视变换失败
【发布时间】:2023-03-03 19:41:01
【问题描述】:

我正在尝试创建一个简单的 iPhone 应用程序,该应用程序显示一张下方带有反射的图片,并且我想使用 Core Animation 围绕 X 轴旋转图片。

我首先使用“基于视图的应用程序”模板创建了一个新的 iPhone 应用程序。我添加了一个“Picture.jpg”图像文件,然后将其添加到视图控制器:

- (void)awakeFromNib {
    CGFloat zDistance = 1500.0f;

    // Create perspective transformation
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = 1.0f / -zDistance;

    // Create perspective transform for reflected layer
    CATransform3D reflectedTransform = CATransform3DMakeRotation(M_PI, 1.0f, 0.0f, 0.0f);
    reflectedTransform.m34 = 1.0f / -zDistance;

    // Create spinning picture
    CALayer *spinningLayer = [CALayer layer];
    spinningLayer.frame = CGRectMake(60.0f, 60.0f, 200.0f, 300.0f);
    spinningLayer.contents = (id)[UIImage imageNamed:@"Picture.jpg"].CGImage;
    spinningLayer.transform = transform;    

    // Create reflection of spinning picture
    CALayer *reflectionLayer = [CALayer layer];
    reflectionLayer.frame = CGRectMake(60.0f, 360.0f, 200.0f, 300.0f);
    reflectionLayer.contents = (id)[UIImage imageNamed:@"Picture.jpg"].CGImage;
    reflectionLayer.opacity = 0.4f;
    reflectionLayer.transform = reflectedTransform;

    // Add layers to the root layer
    [self.view.layer addSublayer:spinningLayer];
    [self.view.layer insertSublayer:reflectionLayer below:spinningLayer];

    // Spin the layers
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    anim.fromValue = [NSNumber numberWithFloat:0.0f];
    anim.toValue = [NSNumber numberWithFloat:(2 * M_PI)];
    anim.duration = 3.0f;
    anim.repeatCount = 1e100f;
    [spinningLayer addAnimation:anim forKey:@"anim"];
    [reflectionLayer addAnimation:anim forKey:@"anim"];
}

几乎有效。问题是主画面和反射的旋转并不完全同步。它非常接近完美,但图片底部和反射顶部的边缘相距几个像素。它们似乎相差几度。

在屏幕左侧,倒影似乎在画面“前面”,而在右侧,倒影在画面后面。换句话说,看起来顶部图像的底角被推向屏幕,而反射的顶角被拉向观看者。在图像旋转时查看图像的正面和背面都是如此。

如果我增加zDistance,效果就不那么明显了。如果我通过将两层的transform.m34 都设为零来完全消除透视变换,那么图片和反射看起来完美同步。所以我认为这个问题与时间同步问题无关。

我怀疑我的视角转换遗漏了一些东西,但我不确定如何确定哪里出了问题。我想我基本上是在进行this related Stack Overflow question 中描述的相同转换。

有人可以帮忙吗?

【问题讨论】:

    标签: iphone core-animation catransform3d


    【解决方案1】:

    我想我可能已经找到了我的问题的答案。在我的原始代码中,我通过将其放在图片下方并应用翻转和透视来创建反射。正确的做法似乎是将倒影放置在与图片相同的位置,应用平移和旋转变换使其到达正确的位置,然后应用透视变换。

    在对旋转进行动画处理时,需要以图片动画的相反方向旋转反射。

    所以,下面是有效的代码:

    - (void)awakeFromNib {
        CGFloat zDistance = 1500.0f;
    
        // Create perspective transformation
        CATransform3D transform = CATransform3DIdentity;
        transform.m34 = 1.0f / -zDistance;
    
        // Create perspective transform for reflected layer
        CATransform3D reflectedTransform = CATransform3DMakeTranslation(0.0f, 300.0f, 0.0f);
        reflectedTransform = CATransform3DRotate(reflectedTransform, M_PI, 1.0f, 0.0f, 0.0f);
        reflectedTransform.m34 = 1.0f / -zDistance;
    
        // Create spinning picture
        CALayer *spinningLayer = [CALayer layer];
        spinningLayer.frame = CGRectMake(60.0f, 60.0f, 200.0f, 300.0f);
        spinningLayer.contents = (id)[UIImage imageNamed:@"Picture.jpg"].CGImage;
        spinningLayer.transform = transform;    
    
        // Create reflection of spinning picture
        CALayer *reflectionLayer = [CALayer layer];
        reflectionLayer.frame = CGRectMake(60.0f, 60.0f, 200.0f, 300.0f);
        reflectionLayer.contents = (id)[UIImage imageNamed:@"Picture.jpg"].CGImage;
        reflectionLayer.opacity = 0.4f;
        reflectionLayer.transform = reflectedTransform;
    
        // Add layers to the root layer
        [self.view.layer addSublayer:spinningLayer];
        [self.view.layer insertSublayer:reflectionLayer below:spinningLayer];
    
        // Spin the layers
        CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
        anim.fromValue = [NSNumber numberWithFloat:0.0f];
        anim.toValue = [NSNumber numberWithFloat:(2 * M_PI)];
        anim.duration = 3.0f;
        anim.repeatCount = 1e100f;
        [spinningLayer addAnimation:anim forKey:@"anim"];
    
        CABasicAnimation *reflectAnim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
        reflectAnim.fromValue = [NSNumber numberWithFloat:0.0f];
        reflectAnim.toValue = [NSNumber numberWithFloat:(-2 * M_PI)];
        reflectAnim.duration = 3.0f;
        reflectAnim.repeatCount = 1e100f;
        [reflectionLayer addAnimation:reflectAnim forKey:@"reflectAnim"];
    }
    

    我不确定为什么会这样。对我来说,将反射放置在与原始图像相同的位置,然后以某种方式对其进行变换,将这两个图像置于同一个“变换空间”中,这对我来说具有直观的意义,但如果有人能解释这背后的数学原理,我将不胜感激。

    完整的项目在 GitHub 上可用:https://github.com/kristopherjohnson/perspectivetest

    【讨论】:

      【解决方案2】:

      您可以使用CAAnimationGroup 对象在同一时间空间上运行两个动画。这应该可以解决任何同步问题。

      编辑:删除了愚蠢的代码。

      【讨论】:

      • 我认为这不是时间同步问题。动画似乎同时运行,但图像之间的接缝“扭曲”。
      • 嗯。可能只是其中一个动画运行得稍微领先于另一个吗?如果反射提前一帧,您会看到接缝处略有不同。不过,这不是我的专业领域,所以我可能是错的。
      • 倒影看起来像是在一侧向前,但在另一侧向后。
      • 您提供的代码旋转视图的根层,而不是单个子层。因此,透视变换不会做任何事情,因为图层不会相对于根图层移动。
      • 我将关键路径更改为指向各个子层,但现在我想起来了,我认为这些名称无法访问它们。嗯。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-28
      • 1970-01-01
      相关资源
      最近更新 更多