How to smooth a freehand drawn SVG path?

后端 未结 3 922
暗喜
暗喜 2021-01-31 10:59

I am looking for a solution to convert a freehand, user drawn SVG path, consisting of lots auf LineTo segments, into a smoother one.

Preferred language would be JavaScri

3条回答
  •  轮回少年
    2021-01-31 11:42

    Let's imagine the user drawing is an array of tuples, we could do something like

    const points = [[100, 50], [50, 15], [5, 60], [10, 20], [20, 10], [30, 190], [40, 10], [50, 60], [60, 120], [70, 10], [80, 50], [90, 50], [120, 10], [150, 80], [160, 10] ]
    
    const lineProperties = (pointA, pointB) => {
      const lengthX = pointB[0] - pointA[0]
      const lengthY = pointB[1] - pointA[1]
      return {
        length: Math.sqrt(Math.pow(lengthX, 2) + Math.pow(lengthY, 2)),
        angle: Math.atan2(lengthY, lengthX)
      }
    }
    
    const controlPointCalc = (current, previous, next, reverse) => {
      const c = current
      const p = previous ? previous : c
      const n = next ? next : c
      const smoothing = 0.2
      const o = lineProperties(p, n)
      const rev = reverse ? Math.PI : 0
    
      const x = c[0] + Math.cos(o.angle + rev) * o.length * smoothing
      const y = c[1] + Math.sin(o.angle + rev) * o.length * smoothing
    
      return [x, y]
    }
    
    const svgPathRender = points => {      
      const d = points.reduce((acc, e, i, a) => {
          if (i > 0) {
            const cs = controlPointCalc(a[i - 1], a[i - 2], e)
            const ce = controlPointCalc(e, a[i - 1], a[i + 1], true)
            return `${acc} C ${cs[0]},${cs[1]} ${ce[0]},${ce[1]} ${e[0]},${e[1]}`
          } else {
            return `${acc} M ${e[0]},${e[1]}`
          }
        },'')
    
      return ``
    }
    
    const svg = document.querySelector('.svg')
    
    svg.innerHTML = svgPathRender(points)
    
    

    Detailed explanations in this article.

提交回复
热议问题