获得CGPath总长度

我有许多UIBezierPath,我用CALyerAnimation @“StrokeStart”和@“strokeEnd”制作动画。

我希望动画对于所有路径都具有相同的速度,所以我可以使用路径的长度:

DISTANCE / TIME = SPEED

有没有办法计算路径“长度”?

谢谢

沙尼

虽然您可以通过数值积分来计算贝塞尔路径的长度,但通过将路径分成线性段可以更容易地完成。 如果片段足够小,则可以忽略近似误差,尤其是您只是想要对其进行动画处理。

我将向您展示四条曲线的function,但您也可以轻松地将立方曲线的解决方案合并到:

- (float) bezierCurveLengthFromStartPoint: (CGPoint) start toEndPoint: (CGPoint) end withControlPoint: (CGPoint) control { const int kSubdivisions = 50; const float step = 1.0f/(float)kSubdivisions; float totalLength = 0.0f; CGPoint prevPoint = start; // starting from i = 1, since for i = 0 calulated point is equal to start point for (int i = 1; i <= kSubdivisions; i++) { float t = i*step; float x = (1.0 - t)*(1.0 - t)*start.x + 2.0*(1.0 - t)*t*control.x + t*t*end.x; float y = (1.0 - t)*(1.0 - t)*start.y + 2.0*(1.0 - t)*t*control.y + t*t*end.y; CGPoint diff = CGPointMake(x - prevPoint.x, y - prevPoint.y); totalLength += sqrtf(diff.x*diff.x + diff.y*diff.y); // Pythagorean prevPoint = CGPointMake(x, y); } return totalLength; } 

编辑

如果您无法访问路径控制点(比如使用弧创建路径),则始终可以使用CGPathApply函数访问基础贝塞尔曲线:

 - (void) testPath { UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointZero]; [path addQuadCurveToPoint:CGPointMake(1, 1) controlPoint:CGPointMake(-2, 2)]; CGPathRef p = path.CGPath; CGPathApply(p, nil, pathFunction); } void pathFunction(void *info, const CGPathElement *element) { if (element->type == kCGPathElementAddQuadCurveToPoint) { CGPoint p; p = element->points[0]; // control point NSLog(@"%lg %lg", px, py); p = element->points[1]; // end point NSLog(@"%lg %lg", px, py); } // check other cases as well! } 

请注意,它不提供路径的起点,但您可以轻松地自行跟踪它。

最近我遇到了同样的问题,但在这种情况下,无论涉及哪些元素,我都需要完整的路径长度。 例如,线条,曲线,弧线,以及上面的解决方案对我没有用,我提出了一个通用的解决方案。

这里是用swift UIBezierPath.length编写的完整代码

这是包含length属性的扩展。

 extension UIBezierPath{ var length: CGFloat{ var pathLength:CGFloat = 0.0 var current = CGPointZero var first = CGPointZero self.CGPath.forEach{ element in pathLength += element.distance(to: current, startPoint: first) if element.type == .MoveToPoint{ first = element.point } if element.type != .CloseSubpath{ current = element.point } } return pathLength } } // eg let path = UIBezierPath() path.lineWidth = 10 path.moveToPoint(CGPoint(x: 10, y: 10)) path.addArcWithCenter( CGPoint(x: 100,y: 50), radius: 50, startAngle: CGFloat(140.0.degrees), endAngle: CGFloat(40.0.degrees), clockwise: false ) path.addLineToPoint(CGPoint(x: 150, y: 30)) path.closePath() print( path.length )