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

如何在SwiftUI中实现手指滑动区域的空心带边框效果?

SwiftUI实现滑动轨迹空心边框效果

核心思路

要实现滑动轨迹区域的空心边框,关键是先生成所有滑动点对应圆的并集区域,再将该区域的轮廓转换为可填充的边框路径,而非直接绘制单个圆的边框。

完整实现代码

struct Circular: View {
    @State private var touchPoints: [CGPoint] = []
    @State private var borderPaths: [Path] = [] // 支持多次拖动叠加边框
    @State private var isDragging = false
    
    // 可自定义配置参数
    private let circleDiameter: CGFloat = 30
    private let borderWidth: CGFloat = 2
    private let dragPointInterval: CGFloat = 10 // 过滤过近的点,优化性能
    
    var body: some View {
        ZStack {
            Image("img4")
                .resizable()
                .scaledToFit()
            
            // 拖动过程中显示临时轨迹(可选,和原效果一致)
            if isDragging {
                Color.red
                    .mask(eraserShape())
                    .blendMode(.colorDodge)
            }
            
            // 所有已完成的拖动边框
            ForEach(0..<borderPaths.count, id: \.self) { index in
                borderPaths[index]
                    .fill(Color.blue) // 边框颜色可自定义
            }
        }
        .gesture(
            DragGesture(minimumDistance: 0)
                .onChanged { value in
                    isDragging = true
                    // 避免添加过于密集的点,减少性能消耗
                    guard let lastPoint = touchPoints.last else {
                        touchPoints.append(value.location)
                        return
                    }
                    let distance = hypot(value.location.x - lastPoint.x, value.location.y - lastPoint.y)
                    if distance >= dragPointInterval {
                        touchPoints.append(value.location)
                    }
                }
                .onEnded { _ in
                    isDragging = false
                    // 生成所有圆的并集路径
                    var unionPath = Path()
                    for point in touchPoints {
                        let rect = CGRect(
                            x: point.x - circleDiameter / 2,
                            y: point.y - circleDiameter / 2,
                            width: circleDiameter,
                            height: circleDiameter
                        )
                        unionPath.addEllipse(in: rect)
                    }
                    // 将并集路径转换为边框路径
                    borderPaths.append(unionPath.strokedPath(StrokeStyle(lineWidth: borderWidth)))
                    touchPoints.removeAll() // 清空当前拖动点,准备下一次操作
                }
        )
    }
    
    private func eraserShape() -> Path {
        var path = Path()
        for point in touchPoints {
            let rect = CGRect(
                x: point.x - circleDiameter / 2,
                y: point.y - circleDiameter / 2,
                width: circleDiameter,
                height: circleDiameter
            )
            path.addEllipse(in: rect)
        }
        return path
    }
}

关键细节说明

  1. 并集路径生成:将所有滑动点对应的椭圆添加到同一个Path中,SwiftUI默认的非零缠绕数填充规则会自动合并重叠区域,得到完整的滑动覆盖范围。
  2. 边框路径转换:调用strokedPath(_:)方法,将并集区域路径转换为描边后的路径——这个路径的填充区域正好是原并集区域的边缘边框,填充后自然呈现空心效果。
  3. 性能优化:通过判断点之间的距离过滤密集点,避免数组过大导致的性能问题。
  4. 多拖动支持:用数组borderPaths保存每次拖动生成的边框,实现多次滑动叠加的效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 13:32:36