You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现拐角随走向弯曲的音频波形(基于UIBezierPath)

嘿,我来帮你搞定这个音频波形拐角弯曲的问题!你的现有代码用addLine(to:)绘制直线段来构建波形,所以所有拐角都是尖锐的直角。根据你的需求,我整理了两种常见的实现方案,你可以根据想要的视觉效果来选:

方案一:给单个波形竖线添加圆角拐角

如果你的波形是由多个垂直短线组成(就像当前代码的逻辑),我们可以把每个短线改成带圆角的小矩形,让顶部和底部的拐角变得圆润。修改后的代码如下:

fileprivate func createPath(with points: [CGPoint], pointCount: Int, in rect: CGRect) -> CGPath {
    let path = UIBezierPath()
    guard pointCount > 0, rect.height > 0, rect.width > 0 else { return path.cgPath }
    
    let minValue = 1 / (rect.height / 2)
    let cornerRadius: CGFloat = 3 // 可以根据视觉需求调整圆角大小
    let scaleX = (rect.width - 1) / CGFloat(pointCount - 1)
    let halfHeight = rect.height / 2
    let scaleY = halfHeight
    var transform = CGAffineTransform(scaleX: scaleX, y: scaleY)
    transform.ty = halfHeight
    
    for index in 0..<(pointCount / 2) {
        var startPoint = points[index * 2]
        var endPoint = startPoint
        endPoint.y = max(endPoint.y, minValue)
        endPoint.y = -endPoint.y
        
        // 计算竖线的宽度,反向抵消缩放影响,保证最终显示宽度合适
        let lineWidth = 1 / scaleX
        
        // 用带圆角的矩形替代原来的直线
        let lineRect = CGRect(
            x: startPoint.x - lineWidth/2,
            y: min(startPoint.y, endPoint.y),
            width: lineWidth,
            height: abs(endPoint.y - startPoint.y)
        )
        let roundedLinePath = UIBezierPath(roundedRect: lineRect, cornerRadius: cornerRadius)
        path.append(roundedLinePath)
    }
    
    path.apply(transform)
    return path.cgPath
}

小说明:

  • 把单条直线换成带圆角的矩形后,每个波形竖线的顶底拐角就会呈现自然的弯曲效果
  • cornerRadius参数可以自由调整,数值越大拐角越圆润
  • 计算lineWidth是为了抵消后续的缩放变换,确保竖线宽度在不同容器尺寸下都保持一致
方案二:绘制连续平滑的波形曲线

如果想要更流畅的连续波形(类似音乐APP里常见的那种平滑音频曲线),可以用贝塞尔曲线替代直线连接离散点,让整个线条的拐角自然弯曲。修改后的代码如下:

fileprivate func createPath(with points: [CGPoint], pointCount: Int, in rect: CGRect) -> CGPath {
    let path = UIBezierPath()
    guard pointCount > 1, rect.height > 0, rect.width > 0 else { return path.cgPath }
    
    let minValue = 1 / (rect.height / 2)
    let scaleX = (rect.width - 1) / CGFloat(pointCount - 1)
    let halfHeight = rect.height / 2
    let scaleY = halfHeight
    var transform = CGAffineTransform(scaleX: scaleX, y: scaleY)
    transform.ty = halfHeight
    
    // 先预处理所有点,调整y值并确保最小值
    let processedPoints = points.enumerated().map { index, point in
        var adjustedPoint = point
        if index % 2 == 0 {
            adjustedPoint.y = max(adjustedPoint.y, minValue)
            adjustedPoint.y = -adjustedPoint.y
        }
        return adjustedPoint
    }
    
    // 从第一个点开始绘制
    path.move(to: processedPoints[0])
    
    // 用二次贝塞尔曲线连接每个点,实现平滑弯曲
    for index in 1..<processedPoints.count {
        let currentPoint = processedPoints[index]
        let prevPoint = processedPoints[index-1]
        // 取两点中点作为控制点,让曲线自然过渡
        let controlPoint = CGPoint(
            x: (prevPoint.x + currentPoint.x)/2,
            y: (prevPoint.y + currentPoint.y)/2
        )
        path.addQuadCurve(to: currentPoint, controlPoint: controlPoint)
    }
    
    path.apply(transform)
    return path.cgPath
}

小说明:

  • 二次贝塞尔曲线通过控制点来实现线条的平滑弯曲,完全消除了直线的尖锐拐角
  • 如果需要更精细的曲线调整,可以改用三次贝塞尔曲线addCurve(to:controlPoint1:controlPoint2:),通过两个控制点来精准控制弯曲走向

额外小提示

  • 如果需要上下对称的完整波形,可以在绘制完上半部分后,镜像绘制下半部分的曲线
  • 调整缩放参数时,可以配合圆角大小或控制点计算,确保在不同尺寸的容器里都能保持理想的视觉效果

内容的提问来源于stack exchange,提问作者David

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 12:37:32