UIBezierPath & CAShapeLayer 初始动画跳转

UIBezierPath & CAShapeLayer initial animation jump

我为我的公司构建了这个:https://github.com/busycm/BZYStrokeTimer,在构建过程中,我注意到一个有趣的 "bug",我在使用 UIBezierPath 时似乎无法缓解。就在动画开始时,路径会向前(或向后,取决于它是否逆时针)跳跃一定数量的像素,而不是从平滑的增量动画开始。我发现真正有趣的是,路径向前跳转多少实际上是 CAShaperLayer.

的线宽值

因此,例如,如果我的贝塞尔曲线路径从 CGRectGetMidX(self.bounds) 开始并且行是 35,则动画实际上从 CGRectGetMidX(self.bounds)+35 开始并且线宽越大,跳跃越明显是。有没有办法摆脱它,以便路径从起点平滑地动画出来?

这是第一帧的图片。这是动画开始后的样子。

然后当我恢复动画并再次暂停时,移动的距离大约是您在图片中看到的距离的1/100。

这是我的贝塞尔路径代码:

- (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];
}

动画代码如下:

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"];

下面是我构建 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;
}   

我认为这里发生的事情是,在动画的这一帧中,您正在绘制一条由一个点组成的线。由于线条具有与之关联的粗细,并且线帽类型为 kCALineCapSquare,因此将呈现为高度和宽度等于线条宽度的正方形。

你可以把它想象成你正在用一个方形标记画一条线,你将拖动标记的中点,使其穿过你指定的曲线中的每个点。对于直线中的第一个点,就好像标记在那一点触地,留下一个正方形。

Here's 不同线帽类型的视觉表示,有望使其更加直观。您可能应该将线帽样式更改为 kCALineCapButt.

旁注: 进行更改后,在这行代码中

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

您可能不必再将 x 坐标偏移 dx/2