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

macOS下如何反转CGPath的点顺序/绘制方向?

在macOS上创建反向裁剪CGPath的方案

嘿,我刚好碰到过类似的问题,给你几个可行的方案试试!你提到的思路完全正确——用覆盖绘制区域的大矩形做基础,搭配反转后的目标路径就能实现镂空效果,核心就是解决CGPath的反转问题,下面分两种方法说明:

方法一:借助NSBezierPath的内置反转能力

macOS的NSBezierPath确实自带reversed()方法,我们可以通过CGPath和NSBezierPath的互转快速实现反转,步骤超简单:

  1. 将目标CGPath转成NSBezierPath
  2. 调用reversed()拿到反转后的贝塞尔路径
  3. 再转回CGPath即可

Swift代码示例:

func reversedCGPath(from path: CGPath) -> CGPath? {
    let bezierPath = NSBezierPath(cgPath: path)
    let reversedBezier = bezierPath.reversed()
    return reversedBezier.cgPath
}

接下来用反转后的路径和大矩形组合成裁剪路径:

// 创建覆盖整个绘制区域的大矩形路径
let fullBounds = CGRect(x: 0, y: 0, width: 2000, height: 2000)
let baseRectPath = CGPath(rect: fullBounds, transform: nil)

// 假设inputPath是你需要镂空的原始CGPath
guard let reversedInputPath = reversedCGPath(from: inputPath) else { return }

// 组合路径:先加大矩形,再加反转后的路径
let clipPath = CGMutablePath()
clipPath.addPath(baseRectPath)
clipPath.addPath(reversedInputPath)

// 关键!设置奇偶填充规则,让重叠区域变成镂空
clipPath.setFillRule(.evenOdd)

方法二:手动遍历CGPath元素实现反转

如果不想依赖NSBezierPath,也可以直接操作CGPath的元素来反转。核心思路是:

  • 先收集原路径的所有绘制元素(移动点、线段、曲线等)
  • 反向遍历这些元素,重新构建路径(注意调整曲线控制点的顺序,确保形状正确)

Swift代码示例:

func manualReversedCGPath(from path: CGPath) -> CGPath {
    let reversedPath = CGMutablePath()
    var pathElements: [CGPathElement] = []
    
    // 先收集原路径的所有元素
    path.applyWithBlock { element in
        pathElements.append(element.pointee)
    }
    
    var currentPoint: CGPoint = .zero
    // 反向遍历元素,重新绘制路径
    for element in pathElements.reversed() {
        switch element.type {
        case .moveToPoint:
            currentPoint = element.points[0]
            reversedPath.move(to: currentPoint)
        case .addLineToPoint:
            reversedPath.addLine(to: element.points[0])
            currentPoint = element.points[0]
        case .addQuadCurveToPoint:
            // 二次贝塞尔曲线反转时,控制点不变,终点和起点互换
            reversedPath.addQuadCurve(to: currentPoint, control: element.points[0])
            currentPoint = element.points[1]
        case .addCurveToPoint:
            // 三次贝塞尔曲线反转时,两个控制点顺序互换,终点和起点互换
            reversedPath.addCurve(to: currentPoint, control1: element.points[1], control2: element.points[0])
            currentPoint = element.points[2]
        case .closeSubpath:
            reversedPath.closeSubpath()
        @unknown default:
            break
        }
    }
    
    return reversedPath
}

注意:这个手动方法需要处理多子路径、复杂曲线的细节,如果你的路径包含大量贝塞尔曲线,调试起来会麻烦一些。优先推荐方法一,简单又可靠~

最后再提醒下:组合路径时一定要设置填充规则,.evenOdd规则会让重叠区域自动变成镂空,完美匹配反向裁剪的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:21:57