简体   繁体   English

3D旋转“反弹”时发生意外的CALayer垂直翻转

[英]Unexpected CALayer Vertical Flipping on 3D Rotation 'Bounce'

I am trying to create a flip transition that contains a grid of CALayer cells that flip over and 'bounce' after they have done so. 我正在尝试创建一个包含CALayer单元格的翻转过渡,翻转后会“反弹”。 However, I am encountering a weird vertical flipping issue shown in this video . 但是,我在此视频中遇到了一个奇怪的垂直翻转问题。 Without the 'bounce' animation at the end, the animation works as expected, so it's nothing to do with the image itself, or the splicing of the image into the individual cell layers before animating. 如果没有最后的“弹跳”动画,该动画将按预期方式工作,因此与图像本身无关,也与动画前将图像拼接到各个单元格层无关。

Animation Code (for each individual CALayer cell) 动画代码(针对每个CALayer单元)

@interface transitionCell : CALayer

@property (nonatomic) UIImage* startImage;
@property (nonatomic) UIImage* endImage;

@property (nonatomic, readonly) BOOL animationDidFinish;
@property (nonatomic, readonly) BOOL isAnimating;

@property (nonatomic) CGFloat zRotateFactor;

@end

@implementation transitionCell

-(void) setStartImage:(UIImage *)startImage {
    self.contents = (__bridge id)startImage.CGImage;
}

-(void) startAnimationWithDuration:(CGFloat)duration { // First half of rotation

    _isAnimating = YES;

    CABasicAnimation* yRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    yRotation.fromValue = @(0);
    yRotation.toValue = @(M_PI*0.5);

    CABasicAnimation* zRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    zRotation.fromValue = @(0);
    zRotation.toValue = @(zRotateMax*_zRotateFactor); // zRotateMax is M_PI*0.2 & _zRotateFactor is a value between -1 and 1.

    CAAnimationGroup* rotations = [CAAnimationGroup animation];
    rotations.duration = duration*0.5;
    rotations.delegate = self;
    rotations.removedOnCompletion = NO;
    rotations.fillMode = kCAFillModeForwards;
    rotations.animations = @[yRotation, zRotation];

    [self addAnimation:rotations forKey:@"startRotation"];
}

-(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { // Second half of rotation

    if (anim == [self animationForKey:@"startRotation"] && flag) {

        self.contents = (__bridge id)_endImage.CGImage;

        CABasicAnimation* yRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
        yRotation.toValue = @(M_PI);
        yRotation.fromValue = @(M_PI*0.5);

        CABasicAnimation* zRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
        zRotation.toValue = @(0);
        zRotation.fromValue = @(zRotateMax*_zRotateFactor);

        CAAnimationGroup* rotations = [CAAnimationGroup animation];
        rotations.duration = anim.duration;
        rotations.removedOnCompletion = NO;
        rotations.fillMode = kCAFillModeForwards;
        rotations.delegate = self;
        rotations.animations = @[yRotation, zRotation];
        [self addAnimation:rotations forKey:@"endRotate"];

    } else if (anim == [self animationForKey:@"endRotate"] && flag) { // The 'Bounce' animation (the one causing the issues)

        CABasicAnimation* yRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
        yRotation.toValue = @(M_PI+(M_PI*0.2));
        yRotation.fromValue = @(M_PI);

        CABasicAnimation* zRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
        zRotation.fromValue = @(0);
        zRotation.toValue = @(-zRotateMax*_zRotateFactor*0.2);

        CAAnimationGroup* rotations = [CAAnimationGroup animation];
        rotations.duration = 0.2;
        rotations.removedOnCompletion = NO;
        rotations.fillMode = kCAFillModeForwards;
        rotations.delegate = self;
        rotations.autoreverses = YES;
        rotations.animations = @[yRotation, zRotation];
        [self addAnimation:rotations forKey:@"endRotate2"];

    } else if (anim  == [self animationForKey:@"endRotate2"] && flag) {
        _animationDidFinish = YES;
    }
}

@end

Initialisation Code (In the parent UIViewController ) 初始化代码(在父UIViewController

-(instancetype) initWithStartImage:(UIImage*)startImage endImage:(UIImage*)endImage {
    if (self = [super init]) {

        CGFloat baseNodeHeight = screenWidth()/baseNumberNodesWidth;

        numNodesHeight = roundf(screenHeight()/baseNodeHeight);
        numNodesWidth = roundf(screenWidth()/baseNodeHeight);

        moveUpdateFreq = moveBaseUpdateFreq/(numNodesWidth*numNodesHeight);
        cellMoveUpdateFreq = cellMoveBaseUpdateFreq/(numNodesWidth*numNodesHeight);

        CGFloat const nodeWidth = screenWidth()/numNodesWidth;
        CGFloat const nodeHeight = screenHeight()/numNodesHeight;

        transition = (transitionType)arc4random_uniform(transitionTypeCount);

        cellArray = [NSMutableArray array];

        for (int x = 0; x < numNodesWidth; x++) {

            [cellArray addObject:[NSMutableArray array]];

            for (int y = 0; y < numNodesHeight; y++) {

                transitionCell* c = [transitionCell layer];
                c.frame = (CGRect){{x*nodeWidth, y*nodeHeight}, {nodeWidth, nodeHeight}};

                CGRect startSubRect = {{c.frame.origin.x, screenHeight()-c.frame.origin.y-c.frame.size.height}, c.frame.size};
                CGRect endSubRect = {{c.frame.origin.x, c.frame.origin.y}, c.frame.size};

                c.startImage = [startImage imageFromSubRect:startSubRect];
                c.endImage = [[endImage flippedVerticalImage] imageFromSubRect:endSubRect];

                c.zRotateFactor = -(((CGFloat)y-((CGFloat)numNodesHeight*0.5))/(CGFloat)numNodesHeight);

                [self.view.layer addSublayer:c];
                [cellArray[x] addObject:c];

            }
        }

    }
    return self;
}

Any ideas what I'm doing wrong? 有什么想法我做错了吗?

Well, I can only assume this is a bug, but nonetheless I have found a quick fix. 好吧,我只能假定这是一个错误,但是尽管如此,我还是找到了一个快速修复方法。 By adding a xRotation animation set to go from pi to pi on the 'bounce' animation, the problem is fixed. 通过添加xRotation动画集以在“反弹”动画上从pi转到pi,此问题已解决。 For example, in the bounce animation code: 例如,在反弹动画代码中:

// Bounce animation

CABasicAnimation* yRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
yRotation.toValue = @(M_PI+(M_PI*bounceFactor));
yRotation.fromValue = @(M_PI);

CABasicAnimation* zRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
zRotation.fromValue = @(0);
zRotation.toValue = @(-zRotateMax*_zRotateFactor*bounceFactor);

// New animation, set to do nothing on the x-axis
CABasicAnimation* xRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
xRotation.toValue = @(M_PI);
xRotation.fromValue = @(M_PI);

CAAnimationGroup* rotations = [CAAnimationGroup animation];
rotations.duration = 0.2;
rotations.removedOnCompletion = NO;
rotations.fillMode = kCAFillModeForwards;
rotations.delegate = self;
rotations.autoreverses = YES;
rotations.animations = @[yRotation, zRotation, xRotation];
[self addAnimation:rotations forKey:@"endRotate2"];

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM