3D 旋转时意外的 CALayer 垂直翻转 'Bounce'
Unexpected CALayer Vertical Flipping on 3D Rotation 'Bounce'
我正在尝试创建一个翻转过渡,其中包含一个由 CALayer
个单元格组成的网格,这些单元格翻转后 'bounce'。但是,我遇到了 this video 中显示的奇怪的垂直翻转问题。如果最后没有 'bounce' 动画,动画会按预期工作,因此它与图像本身无关,也与动画之前将图像拼接成各个单元格层无关。
动画代码(每个单独的 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
初始化代码(在父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;
}
知道我做错了什么吗?
好吧,我只能假设这是一个错误,但我还是找到了一个快速修复方法。通过在 'bounce' 动画上添加 xRotation 动画设置从 pi 到 pi,问题得到解决。比如弹跳动画代码中:
// 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"];
我正在尝试创建一个翻转过渡,其中包含一个由 CALayer
个单元格组成的网格,这些单元格翻转后 'bounce'。但是,我遇到了 this video 中显示的奇怪的垂直翻转问题。如果最后没有 'bounce' 动画,动画会按预期工作,因此它与图像本身无关,也与动画之前将图像拼接成各个单元格层无关。
动画代码(每个单独的 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
初始化代码(在父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;
}
知道我做错了什么吗?
好吧,我只能假设这是一个错误,但我还是找到了一个快速修复方法。通过在 'bounce' 动画上添加 xRotation 动画设置从 pi 到 pi,问题得到解决。比如弹跳动画代码中:
// 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"];