获得CGPath总长度

时间:2012-08-19 06:24:46

标签: ios uibezierpath cgpath

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

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

  

DISTANCE / TIME = SPEED

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

由于

沙尼

2 个答案:

答案 0 :(得分:9)

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

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

- (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", p.x, p.y);

        p = element->points[1]; // end point
        NSLog(@"%lg %lg", p.x, p.y);
    }
    // check other cases as well!
}

请注意,它不提供路径的起点,但很容易自己跟踪它。

答案 1 :(得分:-2)

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

这里是用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
    }
}


// e.g
    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 )