Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CAShapeLayer Animating Path Glitches / Flickers (From Ellipse to Rect and back)

I am running into an issue when I create an explicit animation to change the value of a CAShapeLayer's path from an ellipse to a rect.

In my canvas controller I setup a basic CAShapeLayer and add it to the root view's layer:

CAShapeLayer *aLayer;
aLayer = [CAShapeLayer layer];
aLayer.frame = CGRectMake(100, 100, 100, 100);
aLayer.path = CGPathCreateWithEllipseInRect(aLayer.frame, nil);
aLayer.lineWidth = 10.0f;
aLayer.strokeColor = [UIColor blackColor].CGColor;
aLayer.fillColor = [UIColor clearColor].CGColor;
[self.view.layer addSublayer:aLayer];

Then, when I animate the path I get a strange glitch / flicker in the last few frames of the animation when the shape becomes a rect, and in the first few frames when it animates away from being a rect. The animation is set up as follows:

CGPathRef newPath = CGPathCreateWithRect(aLayer.frame, nil);
[CATransaction lock];
[CATransaction begin];
[CATransaction setAnimationDuration:5.0f];
CABasicAnimation *ba = [CABasicAnimation animationWithKeyPath:@"path"];
ba.autoreverses = YES;
ba.fillMode = kCAFillModeForwards;
ba.repeatCount = HUGE_VALF;
ba.fromValue = (id)aLayer.path;
ba.toValue = (__bridge id)newPath;
[aLayer addAnimation:ba forKey:@"animatePath"];
[CATransaction commit];
[CATransaction unlock];

I have tried many different things like locking / unlocking the CATransaction, playing with various fill modes, etc...

Here's an image of the glitch: http://www.postfl.com/outgoing/renderingglitch.png

A video of what I am experiencing can be found here: http://vimeo.com/37720876

like image 735
C4 - Travis Avatar asked Mar 01 '12 08:03

C4 - Travis


2 Answers

I received this feedback from the quartz-dev list:

David Duncan wrote:

Animating the path of a shape layer is only guaranteed to work when you are animating from like to like. A rectangle is a sequence of lines, while an ellipse is a sequence of arcs (you can see the sequence generated by using CGPathApply), and as such the animation between them isn't guaranteed to look very good, or work well at all.

To do this, you basically have to create an analog of a rectangle by using the same curves that you would use to create an ellipse, but with parameters that would cause the rendering to look like a rectangle. This shouldn't be too difficult (and again, you can use what you get from CGPathApply on the path created with CGPathAddEllipseInRect as a guide), but will likely require some tweaking to get right.

like image 50
C4 - Travis Avatar answered Sep 29 '22 11:09

C4 - Travis


Unfortunately this is a limitation of the otherwise awesome animatable path property of CAShapeLayers.

Basically it tries to interpolate between the two paths. It hits trouble when the destination path and start path have a different number of control points - and curves and straight edges will have this problem.

You can try to minimise the effect by drawing your ellipse as 4 curves instead of a single ellipse, but it still isn't quite right. I haven't found a way to go smoothly from curves to polygons.

You may be able to get most of the way there, then transfer to a fade animation for the last part - this won't look as nice, though.

like image 30
jrturton Avatar answered Sep 29 '22 11:09

jrturton