简体   繁体   中英

UIBezierPath & CAShapeLayer initial animation jump

I built this for my company: https://github.com/busycm/BZYStrokeTimer and during the course of building, I noticed an interesting "bug" that I can't seem to mitigate when using UIBezierPath . Right when the animation starts, the path jumps a certain number of pixels forward (or backwards depending if it's counterclockwise) instead of starting up with a smooth, incremental animation. And what I found that's really interesting is how much the path jumps forward is actually the value of the line width for the CAShaperLayer .

So for example, if my bezier path starts off at CGRectGetMidX(self.bounds) and the line with is 35, the animation actually starts from CGRectGetMidX(self.bounds)+35 and the larger the line width, the more noticeable the jump is. Is there any way to get rid of that so that path will smoothly animate out from the start point?

Here's a picture of the first frame. This is what it looks like immediately after the animation starts. 在此输入图像描述

Then when I resume the animation and pause again, the distance moved is about 1/100th of the distance you see in the picture.

Here's my bezier path code:

- (UIBezierPath *)generatePathWithXInset:(CGFloat)dx withYInset:(CGFloat)dy clockWise:(BOOL)clockwise{
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(CGRectGetMidX(self.bounds)+dx/2, dy/2)];
    [path addLineToPoint:CGPointMake(CGRectGetMaxX(self.bounds)-dx/2, dy/2)];
    [path addLineToPoint:CGPointMake(CGRectGetMaxX(self.bounds)-dx/2, CGRectGetMaxY(self.bounds)-dy/2)];
    [path addLineToPoint:CGPointMake(dx/2, CGRectGetMaxY(self.bounds)-dy/2)];
    [path addLineToPoint:CGPointMake(dx/2, dy/2)];
    [path closePath];
    return clockwise ? path : [path bezierPathByReversingPath];
}

Here's the animation code:

CABasicAnimation *wind = [self generateAnimationWithDuration:self.duration == 0 ? kDefaultDuration : self.duration fromValue:@(self.shapeLayer.strokeStart) toValue:@(self.shapeLayer.strokeEnd) withKeypath:keypath withFillMode:kCAFillModeForwards];
wind.timingFunction = [CAMediaTimingFunction functionWithName:self.timingFunction];
wind.removedOnCompletion = NO;
self.shapeLayer.path = [self generatePathWithXInset:self.lineWidth withYInset:self.lineWidth clockWise:self.clockwise].CGPath;
[self.shapeLayer addAnimation:wind forKey:@"strokeEndAnimation"];

And here's how I construct the CAShapeLayer .

- (CAShapeLayer *)shapeLayer {
    return !_shapeLayer ? _shapeLayer = ({
        CAShapeLayer *layer = [CAShapeLayer layer];
        layer.lineWidth = kDefaultLineWidth;
        layer.fillColor = UIColor.clearColor.CGColor;
        layer.strokeColor = [UIColor blackColor].CGColor;
        layer.lineCap = kCALineCapSquare;
        layer.frame = self.bounds;
        layer.strokeStart = 0;
        layer.strokeEnd = 1;
        layer;
    }) : _shapeLayer;
}   

I think what's happening here is that, in this frame of the animation, you are drawing a line that consists of a single point. Since the line has a thickness associated with it, and the line cap type is kCALineCapSquare , that'll get rendered as a square with height and width equal to the line width.

You can think of it as if you are drawing a line with a square marker, and you are going to drag the midpoint of the marker so that it goes through every point in the curve you specified. For the first point in the line, it's as if the marker touches down at that point, leaving a square behind.

Here's a visual representation the different line cap types that will hopefully make it more intuitive. You should probably change the line cap style to kCALineCapButt .

Sidenote: After you make that change, in this line of code

[path moveToPoint:CGPointMake(CGRectGetMidX(self.bounds)+dx/2, dy/2)];

you probably don't have to offset the x coordinate by dx/2 anymore.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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