File size: 3,144 Bytes
89ce340
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import { SVGPathData } from 'svg-pathdata'
import arcToBezier from 'svg-arc-to-cubic-bezier'

const typeMap = {
  1: 'Z',
  2: 'M',
  4: 'H',
  8: 'V',
  16: 'L',
  32: 'C',
  64: 'S',
  128: 'Q',
  256: 'T',
  512: 'A',
}

/**
 * 简单解析SVG路径
 * @param d SVG path d属性
 */
export const parseSvgPath = (d: string) => {
  const pathData = new SVGPathData(d)

  const ret = pathData.commands.map(item => {
    return { ...item, type: typeMap[item.type] }
  })
  return ret
}

export type SvgPath = ReturnType<typeof parseSvgPath>

/**
 * 解析SVG路径,并将圆弧(A)类型的路径转为三次贝塞尔(C)类型的路径
 * @param d SVG path d属性
 */
export const toPoints = (d: string) => {
  const pathData = new SVGPathData(d)
  
  const points = []
  for (const item of pathData.commands) {
    const type = typeMap[item.type]

    if (item.type === 2 || item.type === 16) {
      points.push({
        x: item.x,
        y: item.y,
        relative: item.relative,
        type,
      })
    }
    if (item.type === 32) {
      points.push({
        x: item.x, 
        y: item.y,
        curve: {
          type: 'cubic',
          x1: item.x1,
          y1: item.y1,
          x2: item.x2,
          y2: item.y2,
        },
        relative: item.relative,
        type,
      })
    }
    else if (item.type === 128) {
      points.push({
        x: item.x, 
        y: item.y,
        curve: {
          type: 'quadratic',
          x1: item.x1,
          y1: item.y1,
        },
        relative: item.relative,
        type,
      })
    }
    else if (item.type === 512) {
      const lastPoint = points[points.length - 1]
      if (!['M', 'L', 'Q', 'C'].includes(lastPoint.type)) continue

      const cubicBezierPoints = arcToBezier({
        px: lastPoint.x as number,
        py: lastPoint.y as number,
        cx: item.x,
        cy: item.y,
        rx: item.rX,
        ry: item.rY,
        xAxisRotation: item.xRot,
        largeArcFlag: item.lArcFlag,
        sweepFlag: item.sweepFlag,
      })
      for (const cbPoint of cubicBezierPoints) {
        points.push({
          x: cbPoint.x, 
          y: cbPoint.y,
          curve: {
            type: 'cubic',
            x1: cbPoint.x1,
            y1: cbPoint.y1,
            x2: cbPoint.x2,
            y2: cbPoint.y2,
          },
          relative: false,
          type: 'C',
        })
      }
    }
    else if (item.type === 1) {
      points.push({ close: true, type })
    }
    else continue
  }
  return points
}

export const getSvgPathRange = (path: string) => {
  try {
    const pathData = new SVGPathData(path)
    const xList = []
    const yList = []
    for (const item of pathData.commands) {
      const x = ('x' in item) ? item.x : 0
      const y = ('y' in item) ? item.y : 0
      xList.push(x)
      yList.push(y)
    }
    return {
      minX: Math.min(...xList),
      minY: Math.min(...yList),
      maxX: Math.max(...xList),
      maxY: Math.max(...yList),
    }
  }
  catch {
    return {
      minX: 0,
      minY: 0,
      maxX: 0,
      maxY: 0,
    }
  }
}

export type SvgPoints = ReturnType<typeof toPoints>