如何在 SwiftUI 中从单一曲线路径开始实现书法效果

问题描述 投票:0回答:1

在 SwiftUI 中,如何从单个弯曲路径/线导出书法风格的形状?

我假设基本算法是:

  • 复制路径。
  • 翻译重复的内容。
  • “合并”/焊接/通过在两条线之间“头对头”和“尾对尾”画一条线来组合两条线

这是该算法的一个简单示例,对应于一支扁笔从左到右并以微小角度握持:

重复,尚未连接:

已连接:

但是我试图转换成书法的形状/路径相当复杂,并且不是由一系列点组成(据我所知 - 它是一系列曲线)。

AsemicWord_Squiggle()
                .stroke(Color.blue, lineWidth: 2)
                .frame(width: 200, height: 100)
                .background(Color.gray).opacity(0.3)

struct AsemicWord_Squiggle: Shape {
    
    func path(in rect: CGRect) -> Path {
        
        var path = Path()
        
        // Rate at which the amplitude will decrease
        // smaller = faster
        let waveAmplitudePowerSeed = CGFloat.random(in: 0.7...0.95)
        // Rate at with the wavelength will increase.
        // larger = more
        let waveLengthPowerSeed = CGFloat.random(in: 1.1...1.3)

        // Start point
        let startingX = rect.minX
        let startingY = rect.midY
        path.move(to: CGPoint(x: rect.minX, y: startingY))
        
        // Amplitude of first wave (used for Y component of first control point)
        var waveAmplitude = CGFloat.random(in: rect.height * 0.4...rect.height * 0.6)
        // Wavelength of first wave
        var waveLength = CGFloat.random(in: rect.width * 0.1...rect.width * 0.2)
        
        let firstWaveControlY = CGFloat.random(in: rect.midY - waveAmplitude...rect.midY + waveAmplitude)
        
        // Draw the first wave segment
        let firstWaveLength = waveLength
        let firstNextX = startingX + firstWaveLength
        let firstControl1 = CGPoint(x: startingX + firstWaveLength / 4, y: firstWaveControlY)
        let firstControl2 = CGPoint(x: startingX + 3 * firstWaveLength / 4, y: startingY - waveAmplitude)
        let firstNextPoint = CGPoint(x: firstNextX, y: startingY)
        
        path.addCurve(to: firstNextPoint, control1: firstControl1, control2: firstControl2)
        
        // Continue drawing the rest of the waves
        var currentX = firstNextX
        while currentX < rect.maxX/2 {
            let nextX = currentX + waveLength
            let control1 = CGPoint(x: currentX + waveLength / 4, y: startingY + waveAmplitude)
            let control2 = CGPoint(x: currentX + 3 * waveLength / 4, y: startingY - waveAmplitude)
            let nextPoint = CGPoint(x: nextX, y: startingY)
            
            waveAmplitude = pow(waveAmplitude, waveAmplitudePowerSeed)
            waveLength = pow(waveLength, waveLengthPowerSeed)
            path.addCurve(to: nextPoint, control1: control1, control2: control2)
            currentX = nextX
        }
        
        return path
    }
}

我希望有一种方法可以在“返回路径”之前简单地添加一些内容,例如:

let path2 = path
path2.translateBy(x,y)
path.weld(path2)
return path

但是这些方法并不存在。

或者,理论上,如果我们从由一系列点/数组组成的路径开始,则可以执行以下伪代码:

let copyA:[CGPoint] = path.getPoints
let copyB:[CGPoint] = path.getPoints.translatedBy(x,y)

let result = Path()
result.moveTo(copyA.first)
for points in copyA { point in
    result.lineTo(point) 
}
result.moveTo(copyB.last)
for points in copyB.reversed() { point in
    result.lineTo(point)
}
result.lineTo(copyA.first)

但是我正在使用的形状并不是我所说的一系列点,即使是,我也无法弄清楚如何获得它们。

chatGPT 还告诉我使用其他不存在的方法,对于这样的问题非常困难且烦人。

从“Asemic_Squiggle”开始,如何做到这一点?可以在 SwiftUI 视图修改器级别完成吗?如果没有,最简单的方法是什么?

swift swiftui cgpath swiftui-path swiftui-shape
1个回答
0
投票

不幸的是,SwiftUI 没有提供任何简单的方法来使用自定义画笔描边路径。如果路径由直线组成,那么您可以在每个点绘制图像(此技术用于在 SwiftUi 上绘制铅笔效果的答案中),但当路径由曲线组成时,这就困难得多。

在这种情况下,笔尖具有几何形状并且完全不透明,因此您可以通过多次描画路径并在每次描画上设置偏移来实现相同的结果。

我将你的笔尖形状转换为像素排列,如下所示:

Pixels

以下是如何使用多个偏移笔画绘制形状:

ZStack {
    let size = CGSize(width: 400, height: 200)
    let path = AsemicWord_Squiggle().path(in: CGRect(origin: .zero, size: size))
    ForEach(0...3, id: \.self) { row in
        ForEach(0...2, id: \.self) { col in
            path
                .offsetBy(
                    dx: (CGFloat(row) / 3) + CGFloat(col),
                    dy: CGFloat(row)
                )
                .stroke()
        }
    }
}
.frame(width: 300, height: 200)
.foregroundStyle(.blue)
.background(.gray.opacity(0.3))

Squiggle

© www.soinside.com 2019 - 2024. All rights reserved.