I am trying to create a fairly simple animation using 6 CALayer
objects, each masked by a path. However, I am running into significant lag spikes when trying to animate them. Here is a video of the animation running. I am able to boost performance by setting shouldRasterize
to YES
, however it results in pixelation of the text, as you can see from this image:
I can correct the pixelation by setting the rasterizationScale
to the screen scale, however that brings back the lag spikes that occurred without rasterization!
Here is my code:
@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
I know the code is very crude, but I've just simplified it for debugging.
The colorImage()
& textBG()
functions just do some Core Graphics rendering to produce the 6 images for the 6 layers. This shouldn't be the source of the problem as drawing is simple and the animation is delayed by a second before starting.
I tried only setting the rasterizationScale
to the screen scale on layers that display text, but this didn't work.
I have also tried to improve performance by removing the two layers underneath the third layer, once it has finished animating, however it hasn't improved performance significantly.
-(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++;
}
}
Any suggestions on how to improve the performance?