如何在图像上使用 shapelayer 和 bezierpath 绘制平滑的徒手画 [英] How can i draw smooth freehand drawing using shapelayer and bezierpath over an image

查看:32
本文介绍了如何在图像上使用 shapelayer 和 bezierpath 绘制平滑的徒手画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

 let path = UIBezierPath()
 shapeLayer.strokeColor = UIColor.blue.cgColor
        shapeLayer.lineWidth = 3
        path.move(to: startPoint)
        path.addLine(to: point)
        shapeLayer.lineJoin = kCALineJoinRound
        shapeLayer.path = path.cgPath
        tempImage.layer.addSublayer(shapeLayer)

我正在使用上面的代码.每次触摸移动时调用上面的代码.我没有得到连续绘图.只有我画的最后一部分是可见的.有什么我在这里遗漏的吗.

I am using the above code. Calling the above code each tome the touch is moved. I am not getting a continuous drawing . only the final portion of my drawing is visible.Is there anything i am missing here.

推荐答案

我使用 UIBezierPathCAShapeLayer 实现了流畅的绘图.这里的方法是有一个临时的贝塞尔路径.如果您想在图像上绘图,您可以尝试这样的操作 drawingLayer.content = yourImage.cgImage.

I achieved a smooth drawing with UIBezierPath and CAShapeLayer. Here the approach is having a temporary bezier path. If you want to draw on top of an image probably you can try something like this drawingLayer.content = yourImage.cgImage.

Swift 4

class DrawingView: UIView {

      private var drawingLayer: CAShapeLayer?
      private var currentPath: UIBezierPath?
      private var temporaryPath: UIBezierPath?
      private var points = [CGPoint]()

      var drawColor = UIColor.blue

      var lineWidth: CGFloat = 2.0

      var opacity: CGFloat = 0.8

      var sublayers: [CALayer] {
        return self.layer.sublayers ?? [CALayer]()
      }

      // MARK: Init

      override public init(frame: CGRect) {
        super.init(frame: frame)

        commonInit()
      }

      required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        commonInit()
      }

      private func commonInit() {
        self.layer.removeAllAnimations()
        self.layer.masksToBounds = true  // Restrict the drawing within the canvas
        self.backgroundColor = UIColor.white
        self.isMultipleTouchEnabled = false
      }

      // MARK: Drawing

      override func draw(_ rect: CGRect) {
        // TODO: This orveriding is still required. Need to find a way to remove this
      }

      override func draw(_ layer: CALayer, in ctx: CGContext) {

        let drawingLayer = self.drawingLayer ?? CAShapeLayer()
        drawingLayer.contentsScale = UIScreen.main.scale

        drawingLayer.lineWidth = lineWidth
        drawingLayer.opacity = Float(opacity)
        drawingLayer.lineJoin = .round
        drawingLayer.lineCap = .round
        drawingLayer.fillColor = UIColor.clear.cgColor
        drawingLayer.miterLimit = 0
        drawingLayer.strokeColor = drawColor.cgColor

        let linePath = UIBezierPath()

        if let tempPath = temporaryPath, let bezierPath = currentPath {
          linePath.append(tempPath)
          linePath.append(bezierPath)
          drawingLayer.path = linePath.cgPath
        }

        if self.drawingLayer == nil {
          self.drawingLayer = drawingLayer
          self.layer.addSublayer(drawingLayer)
        }
      }

      // MARK: - Touches

      override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let point = touches.first?.preciseLocation(in: self) else {
          return
        }
        points.removeAll()
        points.append(point)
      }

      override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let point = touches.first?.preciseLocation(in: self) else {
          return
        }
        points.append(point)
        updatePaths()
        layer.setNeedsDisplay()
      }

      override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

        // single touch support
        if points.count == 1 {
          currentPath = createPathStarting(at: points[0])
          currentPath?.lineWidth = self.lineWidth / 2.0
          currentPath?.addArc(withCenter: points[0], radius: lineWidth / 4.0, startAngle: 0, endAngle: .pi * 2.0, clockwise: true)
        }

        finishPath()
      }

      override func touchesCancelled(_ touches: Set<UITouch>?, with event: UIEvent?) {
        finishPath()
      }

      // MARK: - Bezier paths Management

      private func updatePaths() {

        // update main path
        while points.count > 4 {

          points[3] = CGPoint(x: (points[2].x + points[4].x)/2.0, y: (points[2].y + points[4].y)/2.0)

          if currentPath == nil {
            currentPath = createPathStarting(at: points[0])
          }

          currentPath?.addCurve(to: points[3], controlPoint1: points[1], controlPoint2: points[2])
          points.removeFirst(3)
          temporaryPath = nil
        }

        // build temporary path up to last touch point
        switch points.count {
        case 2:
          temporaryPath = createPathStarting(at: points[0])
          temporaryPath?.addLine(to: points[1])
          break
        case 3:
          temporaryPath = createPathStarting(at: points[0])
          temporaryPath?.addQuadCurve(to: points[2], controlPoint: points[1])
          break
        case 4:
          temporaryPath = createPathStarting(at: points[0])
          temporaryPath?.addCurve(to: points[3], controlPoint1: points[1], controlPoint2: points[2])
          break
        default:
          break
        }
      }

      private func finishPath() {

        // add temp path to current path to reflect the changes in canvas
        if let tempPath = temporaryPath {
          currentPath?.append(tempPath)
        }

        currentPath = nil
      }

      private func createPathStarting(at point: CGPoint) -> UIBezierPath {
        let localPath = UIBezierPath()
        localPath.move(to: point)
        return localPath
      }

这篇关于如何在图像上使用 shapelayer 和 bezierpath 绘制平滑的徒手画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆