如何在SwiftUI中基于单条曲线路径实现书法效果
在SwiftUI中如何将单条曲线路径转换为书法风格的Shape?
核心思路
我认为实现书法风格的基本逻辑应该是:
- 复制原路径
- 平移复制后的路径
- 首尾连接两条路径,合并成闭合的填充形状
比如斜握扁平笔从左到右书写的效果:
未连接状态:两条平行的波浪曲线
连接完成后:两条曲线首尾相连,形成类似书法笔触的闭合形状
但我要处理的AsemicWord_Squiggle形状由复杂贝塞尔曲线构成,并非简单的点序列,直接用想象中的便捷方法(比如路径平移、合并)行不通,因为SwiftUI的Path没有对应的API。
原Shape示例与实现
使用示例
AsemicWord_Squiggle() .stroke(Color.blue, lineWidth: 2) .frame(width: 200, height: 100) .background(Color.gray).opacity(0.3)
效果:一条从左到右振幅逐渐减小、波长逐渐增大的随机波浪曲线
AsemicWord_Squiggle实现代码
struct AsemicWord_Squiggle: Shape { func path(in rect: CGRect) -> Path { var path = Path() // 振幅衰减速率,值越小衰减越快 let waveAmplitudePowerSeed = CGFloat.random(in: 0.7...0.95) // 波长增长速率,值越大增长越多 let waveLengthPowerSeed = CGFloat.random(in: 1.1...1.3) // 起始点 let startingX = rect.minX let startingY = rect.midY path.move(to: CGPoint(x: rect.minX, y: startingY)) // 初始波幅(用于第一个控制点的Y值) var waveAmplitude = CGFloat.random(in: rect.height * 0.4...rect.height * 0.6) // 初始波长 var waveLength = CGFloat.random(in: rect.width * 0.1...rect.width * 0.2) let firstWaveControlY = CGFloat.random(in: rect.midY - waveAmplitude...rect.midY + waveAmplitude) // 绘制第一段曲线 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) // 绘制剩余曲线段 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 } }
尝试过的无效方法
我原本想在返回路径前直接做类似以下的操作,但这些方法在SwiftUI中并不存在:
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)
问题需求
以AsemicWord_Squiggle为例:
- 如何实现书法风格的Shape?
- 能否在SwiftUI视图修饰符层面实现?
- 如果不能,最简单的实现方法是什么?
内容的提问来源于stack exchange,提问作者Nerdy Bunz
相关产品推荐
相关产品推荐

