在 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 视图修改器级别完成吗?如果没有,最简单的方法是什么?
不幸的是,SwiftUI 没有提供任何简单的方法来使用自定义画笔描边路径。如果路径由直线组成,那么您可以在每个点绘制图像(此技术用于在 SwiftUi 上绘制铅笔效果的答案中),但当路径由曲线组成时,这就困难得多。
在这种情况下,笔尖具有几何形状并且完全不透明,因此您可以通过多次描画路径并在每次描画上设置偏移来实现相同的结果。
我将你的笔尖形状转换为像素排列,如下所示:
以下是如何使用多个偏移笔画绘制形状:
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))