首页 > 解决方案 > 如何使用 UIBezierPath 创建带有数据点的曲线图?

问题描述

我正在尝试使用 aUIBezierPath创建折线图。我的目标是为这张图表制作动画,让它看起来像是“波浪状的”。但我似乎无法获得镜像数据的路径。

编辑:添加数据点

let dataPoints: [Double]


func wave(at elapsed: Double) -> UIBezierPath {
    let amplitude = CGFloat(50) - abs(fmod(CGFloat(elapsed/2), 3) - 1.5) * 100
    func f(_ x: Int) -> CGFloat {
        return sin(((CGFloat(dataPoints[x]*100) / bounds.width) + CGFloat(elapsed/2)) * 4 * .pi) * amplitude + bounds.midY
    }
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 0, y: f(0)))
    for x in 1..<dataPoints.count {
        path.addLine(to: CGPoint(x: CGFloat(x), y: f(x)))
    }
    return path
}

标签: iosswiftuikit

解决方案


几个问题:

  1. 你定义了你的f(_:),但没有使用它,而是使用dataPoints(我没有看到你在任何地方填充)。直接使用你的f函数可能是最简单的。

  2. 您正在向路径添加三次贝塞尔曲线,但您的控制点是相同的数据点。这不会提供平滑。控制点实际上应该是您的x值之前和之后的点,沿着与相关曲线相切的线(例如,沿着由数据点定义的线和曲线的斜率,也就是一阶导数)。

    这样做并不难,但简单的解决方案就是使用addLine. 如果你使用足够多的数据点,它会显得很平滑。

  3. 当你这样做时,如果只使用 12 个数据点,它不会是一个很好的平滑正弦曲线。相反,请使用更大的数字(120 或 360 或其他)。为了一条正弦曲线,除非您开始看到性能问题,否则不要担心数据点的数量。

  4. 您的x价值观中的CGPoint价值观可能比您预期的要小。如果您只从 0..<12 开始,则生成的路径将只有 12 点宽;哈哈。使用x值一直遍历相关视图。

所以,你最终可能会得到类似的东西:

func wave(at elapsed: Double) -> UIBezierPath {
    let amplitude = CGFloat(50) - abs(fmod(CGFloat(elapsed/2), 3) - 1.5) * 40
    func f(_ x: Int) -> CGFloat {
        return sin(((CGFloat(x) / bounds.width) + CGFloat(elapsed/2)) * 4 * .pi) * amplitude + bounds.midY
    }
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 0, y: f(0)))
    for x in 1...Int(bounds.width) {
        path.addLine(to: CGPoint(x: CGFloat(x), y: f(x)))
    }
    return path
}

将其与 aCADisplayLink结合以更新曲线,从而产生:

正弦曲线


推荐阅读