如何在 swift 中使用 GraphicContext 获得书法线条笔刷效果
How to get calligraphy line brush effect in swift using GraphicContext
我想添加毛笔效果,如下图Image.for我正在使用的画SwiftyDrawView。
以下是来自 SwiftyDraw 的代码片段
/// Overriding draw(rect:) to stroke paths
override open func draw(_ rect: CGRect) {
super.draw(rect)
guard let context: CGContext = UIGraphicsGetCurrentContext() else { return }
for line in lines {
context.setLineCap(.round)
context.setLineJoin(.round)
context.setLineWidth(line.brush.width)
// set blend mode so an eraser actually erases stuff
context.setBlendMode(line.brush.blendMode)
context.setAlpha(line.brush.opacity)
context.setStrokeColor(line.brush.color.cgColor)
context.addPath(line.path)
context.strokePath()
}
}
// 触摸开始
override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isEnabled, let touch = touches.first else { return }
if #available(iOS 9.1, *) {
guard allowedTouchTypes.flatMap({ [=12=].uiTouchTypes }).contains(touch.type) else { return }
}
guard delegate?.swiftyDraw(shouldBeginDrawingIn: self, using: touch) ?? true else { return }
delegate?.swiftyDraw(didBeginDrawingIn: self, using: touch)
setTouchPoints(touch, view: self)
let newLine = Line(path: CGMutablePath(),
brush: Brush(color: brush.color, width: brush.width, opacity: brush.opacity, blendMode: brush.blendMode))
newLine.path.addPath(createNewPath())
lines.append(newLine)
drawingHistory = lines // adding a new line should also update history
}
和 touchesMoved
override open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isEnabled, let touch = touches.first else { return }
if #available(iOS 9.1, *) {
guard allowedTouchTypes.flatMap({ [=13=].uiTouchTypes }).contains(touch.type) else { return }
}
delegate?.swiftyDraw(isDrawingIn: self, using: touch)
updateTouchPoints(for: touch, in: self)
let newPath = createNewPath()
if let currentPath = lines.last {
currentPath.path.addPath(newPath)
}
}
触摸结束
override open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isEnabled, let touch = touches.first else { return }
delegate?.swiftyDraw(didFinishDrawingIn: self, using: touch)
}
和触摸取消
override open func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isEnabled, let touch = touches.first else { return }
delegate?.swiftyDraw(didCancelDrawingIn: self, using: touch)
}
// 并设置 TouchPint
private func setTouchPoints(_ touch: UITouch,view: UIView) {
previousPoint = touch.previousLocation(in: view)
previousPreviousPoint = touch.previousLocation(in: view)
currentPoint = touch.location(in: view)
}
// 和 updateTouchPoints
private func updateTouchPoints(for touch: UITouch,in view: UIView) {
previousPreviousPoint = previousPoint
previousPoint = touch.previousLocation(in: view)
currentPoint = touch.location(in: view)
}
并创建新路径
private func createNewPath() -> CGMutablePath {
let midPoints = getMidPoints()
let subPath = createSubPath(midPoints.0, mid2: midPoints.1)
let newPath = addSubPathToPath(subPath)
return newPath
}
并计算中点
private func calculateMidPoint(_ p1 : CGPoint, p2 : CGPoint) -> CGPoint {
return CGPoint(x: (p1.x + p2.x) * 0.5, y: (p1.y + p2.y) * 0.5);
}
和getMidPoints
private func getMidPoints() -> (CGPoint, CGPoint) {
let mid1 : CGPoint = calculateMidPoint(previousPoint, p2: previousPreviousPoint)
let mid2 : CGPoint = calculateMidPoint(currentPoint, p2: previousPoint)
return (mid1, mid2)
}
并创建子路径
private func createSubPath(_ mid1: CGPoint, mid2: CGPoint) -> CGMutablePath {
let subpath : CGMutablePath = CGMutablePath()
subpath.move(to: CGPoint(x: mid1.x, y: mid1.y))
subpath.addQuadCurve(to: CGPoint(x: mid2.x, y: mid2.y), control: CGPoint(x: previousPoint.x, y: previousPoint.y))
return subpath
}
并添加 SubPathToPath
private func addSubPathToPath(_ subpath: CGMutablePath) -> CGMutablePath {
let bounds : CGRect = subpath.boundingBox
let drawBox : CGRect = bounds.insetBy(dx: -2.0 * brush.width, dy: -2.0 * brush.width)
self.setNeedsDisplay(drawBox)
return subpath
}
我找到了简单的解决方案:
private func createSubPath(_ mid1: CGPoint, mid2: CGPoint) -> CGMutablePath {
let subpath : CGMutablePath = CGMutablePath()
subpath.move(to: CGPoint(x: mid1.x, y: mid1.y))
subpath.addQuadCurve(to: CGPoint(x: mid2.x, y: mid2.y), control: CGPoint(x: previousPoint.x, y: previousPoint.y))
for i in 1 ..< 6 {
subpath.addLines(between: [CGPoint(x: mid1.x + CGFloat(i), y: mid1.y + CGFloat(i)),CGPoint(x: mid2.x+CGFloat(i), y: mid2.y + CGFloat(i))])
}
return subpath
}
我想添加毛笔效果,如下图Image.for我正在使用的画SwiftyDrawView。
以下是来自 SwiftyDraw 的代码片段
/// Overriding draw(rect:) to stroke paths
override open func draw(_ rect: CGRect) {
super.draw(rect)
guard let context: CGContext = UIGraphicsGetCurrentContext() else { return }
for line in lines {
context.setLineCap(.round)
context.setLineJoin(.round)
context.setLineWidth(line.brush.width)
// set blend mode so an eraser actually erases stuff
context.setBlendMode(line.brush.blendMode)
context.setAlpha(line.brush.opacity)
context.setStrokeColor(line.brush.color.cgColor)
context.addPath(line.path)
context.strokePath()
}
}
// 触摸开始
override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isEnabled, let touch = touches.first else { return }
if #available(iOS 9.1, *) {
guard allowedTouchTypes.flatMap({ [=12=].uiTouchTypes }).contains(touch.type) else { return }
}
guard delegate?.swiftyDraw(shouldBeginDrawingIn: self, using: touch) ?? true else { return }
delegate?.swiftyDraw(didBeginDrawingIn: self, using: touch)
setTouchPoints(touch, view: self)
let newLine = Line(path: CGMutablePath(),
brush: Brush(color: brush.color, width: brush.width, opacity: brush.opacity, blendMode: brush.blendMode))
newLine.path.addPath(createNewPath())
lines.append(newLine)
drawingHistory = lines // adding a new line should also update history
}
和 touchesMoved
override open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isEnabled, let touch = touches.first else { return }
if #available(iOS 9.1, *) {
guard allowedTouchTypes.flatMap({ [=13=].uiTouchTypes }).contains(touch.type) else { return }
}
delegate?.swiftyDraw(isDrawingIn: self, using: touch)
updateTouchPoints(for: touch, in: self)
let newPath = createNewPath()
if let currentPath = lines.last {
currentPath.path.addPath(newPath)
}
}
触摸结束
override open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isEnabled, let touch = touches.first else { return }
delegate?.swiftyDraw(didFinishDrawingIn: self, using: touch)
}
和触摸取消
override open func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
guard isEnabled, let touch = touches.first else { return }
delegate?.swiftyDraw(didCancelDrawingIn: self, using: touch)
}
// 并设置 TouchPint
private func setTouchPoints(_ touch: UITouch,view: UIView) {
previousPoint = touch.previousLocation(in: view)
previousPreviousPoint = touch.previousLocation(in: view)
currentPoint = touch.location(in: view)
}
// 和 updateTouchPoints
private func updateTouchPoints(for touch: UITouch,in view: UIView) {
previousPreviousPoint = previousPoint
previousPoint = touch.previousLocation(in: view)
currentPoint = touch.location(in: view)
}
并创建新路径
private func createNewPath() -> CGMutablePath {
let midPoints = getMidPoints()
let subPath = createSubPath(midPoints.0, mid2: midPoints.1)
let newPath = addSubPathToPath(subPath)
return newPath
}
并计算中点
private func calculateMidPoint(_ p1 : CGPoint, p2 : CGPoint) -> CGPoint {
return CGPoint(x: (p1.x + p2.x) * 0.5, y: (p1.y + p2.y) * 0.5);
}
和getMidPoints
private func getMidPoints() -> (CGPoint, CGPoint) {
let mid1 : CGPoint = calculateMidPoint(previousPoint, p2: previousPreviousPoint)
let mid2 : CGPoint = calculateMidPoint(currentPoint, p2: previousPoint)
return (mid1, mid2)
}
并创建子路径
private func createSubPath(_ mid1: CGPoint, mid2: CGPoint) -> CGMutablePath {
let subpath : CGMutablePath = CGMutablePath()
subpath.move(to: CGPoint(x: mid1.x, y: mid1.y))
subpath.addQuadCurve(to: CGPoint(x: mid2.x, y: mid2.y), control: CGPoint(x: previousPoint.x, y: previousPoint.y))
return subpath
}
并添加 SubPathToPath
private func addSubPathToPath(_ subpath: CGMutablePath) -> CGMutablePath {
let bounds : CGRect = subpath.boundingBox
let drawBox : CGRect = bounds.insetBy(dx: -2.0 * brush.width, dy: -2.0 * brush.width)
self.setNeedsDisplay(drawBox)
return subpath
}
我找到了简单的解决方案:
private func createSubPath(_ mid1: CGPoint, mid2: CGPoint) -> CGMutablePath {
let subpath : CGMutablePath = CGMutablePath()
subpath.move(to: CGPoint(x: mid1.x, y: mid1.y))
subpath.addQuadCurve(to: CGPoint(x: mid2.x, y: mid2.y), control: CGPoint(x: previousPoint.x, y: previousPoint.y))
for i in 1 ..< 6 {
subpath.addLines(between: [CGPoint(x: mid1.x + CGFloat(i), y: mid1.y + CGFloat(i)),CGPoint(x: mid2.x+CGFloat(i), y: mid2.y + CGFloat(i))])
}
return subpath
}