Pergunta

Estou tentando criar um aplicativo para iPhone simples que exibe uma imagem com um reflexo abaixo, e quero girar a imagem em torno do eixo X, usando a animação do núcleo.

Comecei criando um novo aplicativo para iPhone usando o modelo "Aplicativo baseado em exibição". Eu adicionei um arquivo de imagem "Picture.jpg" e depois adicionei isso ao controlador de exibição:

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

este quase funciona. O problema é que a fiação da imagem principal e a reflexão não é perfeitamente sincronizada. É muito próximo do perfeito, mas as bordas da parte inferior da imagem e do topo da reflexão estão a alguns pixels. Eles parecem diferir em alguns graus.

No lado esquerdo da tela, o reflexo parece estar "à frente" da imagem e, no lado direito, o reflexo está por trás da imagem. Em outras palavras, parece que os cantos inferiores da imagem superior são empurrados em direção à tela, enquanto os cantos superiores da reflexão são puxados em direção ao espectador. Isso é verdade ao olhar para a frente e na parte de trás das imagens enquanto giram.

Se eu aumentar zDistance, o efeito é menos perceptível. Se eu eliminar completamente as transformações de perspectiva, saindo transform.m34 Igual a zero para ambas as camadas, então a imagem e a reflexão parecem ser perfeitamente sincronizadas. Portanto, não acho que o problema esteja relacionado aos problemas de sincronização do tempo.

Suspeito que minhas transformações de perspectiva estão faltando alguma coisa, mas não tenho certeza de como determinar o que está errado. Eu acho que estou basicamente fazendo a mesma transformação descrita em Esta questão de transbordamento de pilha relacionada.

Alguém pode ajudar?

Foi útil?

Solução

Eu acho que posso ter encontrado uma resposta para minha pergunta. No meu código original, criei a reflexão colocando -a embaixo da figura e aplicando o flip e a perspectiva. A maneira certa de fazer isso parece ser colocar a reflexão na mesma posição que a imagem, aplicar as transformações de tradução e rotação para levá -la ao lugar certo e, em seguida, aplicar a transformação da perspectiva.

Ao animar a rotação, é necessário girar a reflexão na direção oposta da animação da imagem.

Então, aqui está o código que funciona:

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

Não sei exatamente por que isso funciona. Faz sentido intuitivo para mim que colocar a reflexão na mesma posição que o original e depois transformá -lo de alguma forma coloca as duas imagens no mesmo "espaço de transformação", mas eu apreciaria se alguém pudesse explicar a matemática por trás disso.

O projeto completo está disponível no Github: https://github.com/kristopherjohnson/perspectiveTest

Outras dicas

Você pode usar um CAAnimationGroup Objeta -se a executar duas animações no mesmo espaço de tempo. Isso deve cuidar de quaisquer problemas de sincronização.

EDITAR: Removido código bobo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top