多个 CALayer 蒙版导致性能问题
Multiple CALayer masks causing performance issues
我正在尝试使用 6 个 CALayer
对象创建一个相当简单的动画,每个对象都由一条路径遮盖。但是,我 运行 在尝试为它们制作动画时遇到明显的滞后峰值。 Here is a video of the animation running. 我可以通过将 shouldRasterize
设置为 YES
来提高性能,但是它会导致文本像素化,正如您从这张图片中看到的那样:
我可以通过将 rasterizationScale
设置为屏幕比例来校正像素化,但是这会带回没有光栅化时发生的滞后峰值!
这是我的代码:
@interface splashLayer : CALayer
@end
@implementation splashLayer {
UIColor* color;
CALayer* l1, *l2, *l3, *l4, *l5, *l6;
CAShapeLayer* m1, *m2, *m3, *m4, *m5, *m6;
NSUInteger i;
}
-(instancetype) init {
if (self = [super init]) {
color = [lzyColors purpleColor];
i = 0;
m1 = [CAShapeLayer layer]; m2 = [CAShapeLayer layer]; m3 = [CAShapeLayer layer]; m4 = [CAShapeLayer layer]; m5 = [CAShapeLayer layer]; m6 = [CAShapeLayer layer];
self.shouldRasterize = YES;
self.rasterizationScale = screenScale(); // Slows down performance, but stops ugly pixelation.
CGMutablePathRef p = CGPathCreateMutable();
CGFloat const meanScreenLength = (screenHeight()+screenWidth())*0.5;
CGFloat const pythag = lzyMathsPythag(meanScreenLength, meanScreenLength);
CGFloat const halfPythag = pythag*0.5;
CGFloat const pythagHalfPythag = lzyMathsPythag(halfPythag, halfPythag);
CGPoint const center = screenCenter();
CGPoint p1 = {center.x, center.y-pythagHalfPythag};
CGPoint p2 = {center.x+pythagHalfPythag, center.y};
CGPoint p3 = {center.x, center.y+pythagHalfPythag};
CGPoint p4 = {center.x-pythagHalfPythag, center.y};
CGPathMoveToPoint(p, nil, p1.x, p1.y);
lzyCGPathAddLineToPath(p, p2);
lzyCGPathAddLineToPath(p, p3);
lzyCGPathAddLineToPath(p, p4);
CGPathCloseSubpath(p);
m1.path = p; m2.path = p; m3.path = p; m4.path = p; m5.path = p; m6.path = p;
CGPathRelease(p);
m1.position = (CGPoint){-pythag, -pythag}; m2.position = (CGPoint){-pythag, -pythag}; m3.position = (CGPoint){-pythag, -pythag};
m4.position = (CGPoint){pythag, pythag}; m5.position = (CGPoint){pythag, pythag}; m6.position = (CGPoint){pythag, pythag};
l1 = [CALayer layer];
l1.contents = (__bridge id _Nullable)(colorImage([color lightenByValue:0.6], screenSize()).CGImage);
l1.frame = (CGRect){CGPointZero, screenSize()};
l1.mask = m1;
l2 = [CALayer layer];
l2.contents = (__bridge id _Nullable)(textBG([color lightenByValue:0.3], screenSize()).CGImage);
l2.frame = (CGRect){CGPointZero, screenSize()};
l2.mask = m2;
l3 = [CALayer layer];
// l3.rasterizationScale = screenScale(); (Doesn't work)
l3.contents = (__bridge id _Nullable)(textBG(color, screenSize()).CGImage);
l3.frame = (CGRect){CGPointZero, screenSize()};
l3.mask = m3;
UIColor* color2 = [lzyColors redColor];
l4 = [CALayer layer];
l4.contents = (__bridge id _Nullable)(colorImage([color2 lightenByValue:0.6], screenSize()).CGImage);
l4.frame = (CGRect){CGPointZero, screenSize()};
l4.mask = m4;
l5 = [CALayer layer];
l5.contents = (__bridge id _Nullable)(colorImage([color2 lightenByValue:0.3], screenSize()).CGImage);
l5.frame = (CGRect){CGPointZero, screenSize()};
l5.mask = m5;
l6 = [CALayer layer];
l6.contents = (__bridge id _Nullable)(colorImage(color2, screenSize()).CGImage);
l6.frame = (CGRect){CGPointZero, screenSize()};
l6.mask = m6;
[self addSublayer:l1]; [self addSublayer:l2]; [self addSublayer:l3]; [self addSublayer:l4]; [self addSublayer:l5]; [self addSublayer:l6];
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"position"];
anim.fromValue = [NSValue valueWithCGPoint:(CGPoint){-pythag, -pythag}];
anim.toValue = [NSValue valueWithCGPoint:CGPointZero];
anim.delegate = self;
anim.beginTime = CACurrentMediaTime()+1;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.removedOnCompletion = NO;
anim.fillMode = kCAFillModeForwards;
anim.duration = 1;
[m1 addAnimation:anim forKey:@"0"];
anim.duration = 1.25;
[m2 addAnimation:anim forKey:@"1"];
anim.duration = 1.5;
[m3 addAnimation:anim forKey:@"2"];
anim.fromValue = [NSValue valueWithCGPoint:(CGPoint){pythag, pythag}];
anim.beginTime = CACurrentMediaTime()+2.5;
anim.duration = 1;
[m4 addAnimation:anim forKey:@"3"];
anim.duration = 1.25;
[m5 addAnimation:anim forKey:@"4"];
anim.duration = 1.5;
[m6 addAnimation:anim forKey:@"5"];
}
return self;
}
@end
我知道代码很粗糙,但为了调试我只是简化了它。
colorImage()
和 textBG()
函数只是做一些 Core Graphics 渲染,为 6 个层生成 6 个图像。这不应该是问题的根源,因为绘图很简单,动画在开始前延迟一秒。
我尝试只在显示文本的图层上将 rasterizationScale
设置为屏幕比例,但这没有用。
我还尝试通过移除第三层下面的两层来提高性能,一旦它完成动画,但它并没有显着提高性能。
-(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
if (flag) {
if (i == 2) {
[l1 removeFromSuperlayer];
l1 = nil;
m1 = nil;
[l2 removeFromSuperlayer];
l2 = nil;
m2 = nil;
l3.mask = nil;
m3 = nil;
}
i++;
}
}
关于如何提高性能有什么建议吗?
我建议将图层和蒙版合成为静态图像并制作动画。那应该很快。
好吧,在尝试了无数种方法之后,终于实现了这个动画;我得出的结论是,我可能最好直接跳到 openGL。这将使我能够创建一个更通用的解决方案来解决更复杂的动画效果更好的问题。
我正在尝试使用 6 个 CALayer
对象创建一个相当简单的动画,每个对象都由一条路径遮盖。但是,我 运行 在尝试为它们制作动画时遇到明显的滞后峰值。 Here is a video of the animation running. 我可以通过将 shouldRasterize
设置为 YES
来提高性能,但是它会导致文本像素化,正如您从这张图片中看到的那样:
我可以通过将 rasterizationScale
设置为屏幕比例来校正像素化,但是这会带回没有光栅化时发生的滞后峰值!
这是我的代码:
@interface splashLayer : CALayer
@end
@implementation splashLayer {
UIColor* color;
CALayer* l1, *l2, *l3, *l4, *l5, *l6;
CAShapeLayer* m1, *m2, *m3, *m4, *m5, *m6;
NSUInteger i;
}
-(instancetype) init {
if (self = [super init]) {
color = [lzyColors purpleColor];
i = 0;
m1 = [CAShapeLayer layer]; m2 = [CAShapeLayer layer]; m3 = [CAShapeLayer layer]; m4 = [CAShapeLayer layer]; m5 = [CAShapeLayer layer]; m6 = [CAShapeLayer layer];
self.shouldRasterize = YES;
self.rasterizationScale = screenScale(); // Slows down performance, but stops ugly pixelation.
CGMutablePathRef p = CGPathCreateMutable();
CGFloat const meanScreenLength = (screenHeight()+screenWidth())*0.5;
CGFloat const pythag = lzyMathsPythag(meanScreenLength, meanScreenLength);
CGFloat const halfPythag = pythag*0.5;
CGFloat const pythagHalfPythag = lzyMathsPythag(halfPythag, halfPythag);
CGPoint const center = screenCenter();
CGPoint p1 = {center.x, center.y-pythagHalfPythag};
CGPoint p2 = {center.x+pythagHalfPythag, center.y};
CGPoint p3 = {center.x, center.y+pythagHalfPythag};
CGPoint p4 = {center.x-pythagHalfPythag, center.y};
CGPathMoveToPoint(p, nil, p1.x, p1.y);
lzyCGPathAddLineToPath(p, p2);
lzyCGPathAddLineToPath(p, p3);
lzyCGPathAddLineToPath(p, p4);
CGPathCloseSubpath(p);
m1.path = p; m2.path = p; m3.path = p; m4.path = p; m5.path = p; m6.path = p;
CGPathRelease(p);
m1.position = (CGPoint){-pythag, -pythag}; m2.position = (CGPoint){-pythag, -pythag}; m3.position = (CGPoint){-pythag, -pythag};
m4.position = (CGPoint){pythag, pythag}; m5.position = (CGPoint){pythag, pythag}; m6.position = (CGPoint){pythag, pythag};
l1 = [CALayer layer];
l1.contents = (__bridge id _Nullable)(colorImage([color lightenByValue:0.6], screenSize()).CGImage);
l1.frame = (CGRect){CGPointZero, screenSize()};
l1.mask = m1;
l2 = [CALayer layer];
l2.contents = (__bridge id _Nullable)(textBG([color lightenByValue:0.3], screenSize()).CGImage);
l2.frame = (CGRect){CGPointZero, screenSize()};
l2.mask = m2;
l3 = [CALayer layer];
// l3.rasterizationScale = screenScale(); (Doesn't work)
l3.contents = (__bridge id _Nullable)(textBG(color, screenSize()).CGImage);
l3.frame = (CGRect){CGPointZero, screenSize()};
l3.mask = m3;
UIColor* color2 = [lzyColors redColor];
l4 = [CALayer layer];
l4.contents = (__bridge id _Nullable)(colorImage([color2 lightenByValue:0.6], screenSize()).CGImage);
l4.frame = (CGRect){CGPointZero, screenSize()};
l4.mask = m4;
l5 = [CALayer layer];
l5.contents = (__bridge id _Nullable)(colorImage([color2 lightenByValue:0.3], screenSize()).CGImage);
l5.frame = (CGRect){CGPointZero, screenSize()};
l5.mask = m5;
l6 = [CALayer layer];
l6.contents = (__bridge id _Nullable)(colorImage(color2, screenSize()).CGImage);
l6.frame = (CGRect){CGPointZero, screenSize()};
l6.mask = m6;
[self addSublayer:l1]; [self addSublayer:l2]; [self addSublayer:l3]; [self addSublayer:l4]; [self addSublayer:l5]; [self addSublayer:l6];
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"position"];
anim.fromValue = [NSValue valueWithCGPoint:(CGPoint){-pythag, -pythag}];
anim.toValue = [NSValue valueWithCGPoint:CGPointZero];
anim.delegate = self;
anim.beginTime = CACurrentMediaTime()+1;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.removedOnCompletion = NO;
anim.fillMode = kCAFillModeForwards;
anim.duration = 1;
[m1 addAnimation:anim forKey:@"0"];
anim.duration = 1.25;
[m2 addAnimation:anim forKey:@"1"];
anim.duration = 1.5;
[m3 addAnimation:anim forKey:@"2"];
anim.fromValue = [NSValue valueWithCGPoint:(CGPoint){pythag, pythag}];
anim.beginTime = CACurrentMediaTime()+2.5;
anim.duration = 1;
[m4 addAnimation:anim forKey:@"3"];
anim.duration = 1.25;
[m5 addAnimation:anim forKey:@"4"];
anim.duration = 1.5;
[m6 addAnimation:anim forKey:@"5"];
}
return self;
}
@end
我知道代码很粗糙,但为了调试我只是简化了它。
colorImage()
和 textBG()
函数只是做一些 Core Graphics 渲染,为 6 个层生成 6 个图像。这不应该是问题的根源,因为绘图很简单,动画在开始前延迟一秒。
我尝试只在显示文本的图层上将 rasterizationScale
设置为屏幕比例,但这没有用。
我还尝试通过移除第三层下面的两层来提高性能,一旦它完成动画,但它并没有显着提高性能。
-(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
if (flag) {
if (i == 2) {
[l1 removeFromSuperlayer];
l1 = nil;
m1 = nil;
[l2 removeFromSuperlayer];
l2 = nil;
m2 = nil;
l3.mask = nil;
m3 = nil;
}
i++;
}
}
关于如何提高性能有什么建议吗?
我建议将图层和蒙版合成为静态图像并制作动画。那应该很快。
好吧,在尝试了无数种方法之后,终于实现了这个动画;我得出的结论是,我可能最好直接跳到 openGL。这将使我能够创建一个更通用的解决方案来解决更复杂的动画效果更好的问题。