美文网首页
svg转成坐标数组

svg转成坐标数组

作者: 凡凡的小web | 来源:发表于2019-03-11 17:54 被阅读0次

    function parsePathString(pathString) {

      if (!pathString) {

        return null

      }

      var pathCommand = /([a-z])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/ig,

        pathValues = /(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\s]*,?[\s]*/ig,

        paramCounts = {a: 7, c: 6, o: 2, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, u: 3, z: 0},

        data = []

      if (!data.length) {

        String(pathString).replace(pathCommand, function(a, b, c) {

          var params = [],

            name = b.toLowerCase()

          c.replace(pathValues, function(a, b) {

            b && params.push(+b)

          })

          if (name == 'm' && params.length > 2) {

            data.push([b].concat(params.splice(0, 2)))

            name = 'l'

            b = b == 'm' ? 'l' : 'L'

          }

          if (name == 'o' && params.length == 1) {

            data.push([b, params[0]])

          }

          if (name == 'r') {

            data.push([b].concat(params))

          } else {

            while (params.length >= paramCounts[name]) {

              data.push([b].concat(params.splice(0, paramCounts[name])))

              if (!paramCounts[name]) {

                break

              }

            }

          }

        })

      }

      return data

    }

    function pathToAbsolute(pathArray) {

      pathArray = parsePathString(pathArray)

      var res = [],

        x = 0,

        y = 0,

        mx = 0,

        my = 0,

        start = 0,

        pa0

      if (pathArray[0][0] == 'M') {

        x = +pathArray[0][1]

        y = +pathArray[0][2]

        mx = x

        my = y

        start++

        res[0] = ['M', x, y]

      }

      var crz = pathArray.length == 3 &&

                pathArray[0][0] == 'M' &&

                pathArray[1][0].toUpperCase() == 'R' &&

                pathArray[2][0].toUpperCase() == 'Z'

      for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {

        res.push(r = [])

        pa = pathArray[i]

        pa0 = pa[0]

        if (pa0 != pa0.toUpperCase()) {

          r[0] = pa0.toUpperCase()

          switch (r[0]) {

            case 'A':

              r[1] = pa[1]

              r[2] = pa[2]

              r[3] = pa[3]

              r[4] = pa[4]

              r[5] = pa[5]

              r[6] = +pa[6] + x

              r[7] = +pa[7] + y

              break

            case 'V':

              r[1] = +pa[1] + y

              break

            case 'H':

              r[1] = +pa[1] + x

              break

            case 'R':

              var dots = [x, y].concat(pa.slice(1))

              for (var j = 2, jj = dots.length; j < jj; j++) {

                dots[j] = +dots[j] + x

                dots[++j] = +dots[j] + y

              }

              res.pop()

              res = res.concat(catmullRom2bezier(dots, crz))

              break

            case 'O':

              res.pop()

              dots = ellipsePath(x, y, pa[1], pa[2])

              dots.push(dots[0])

              res = res.concat(dots)

              break

            case 'U':

              res.pop()

              res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3]))

              r = ['U'].concat(res[res.length - 1].slice(-2))

              break

            case 'M':

              mx = +pa[1] + x

              my = +pa[2] + y

              break

            default:

              for (j = 1, jj = pa.length; j < jj; j++) {

                r[j] = +pa[j] + (j % 2 ? x : y)

              }

          }

        } else if (pa0 == 'R') {

          dots = [x, y].concat(pa.slice(1))

          res.pop()

          res = res.concat(catmullRom2bezier(dots, crz))

          r = ['R'].concat(pa.slice(-2))

        } else if (pa0 == 'O') {

          res.pop()

          dots = ellipsePath(x, y, pa[1], pa[2])

          dots.push(dots[0])

          res = res.concat(dots)

        } else if (pa0 == 'U') {

          res.pop()

          res = res.concat(ellipsePath(x, y, pa[1], pa[2], pa[3]))

          r = ['U'].concat(res[res.length - 1].slice(-2))

        } else {

          for (var k = 0, kk = pa.length; k < kk; k++) {

            r[k] = pa[k]

          }

        }

        pa0 = pa0.toUpperCase()

        if (pa0 != 'O') {

          switch (r[0]) {

            case 'Z':

              x = +mx

              y = +my

              break

            case 'H':

              x = r[1]

              break

            case 'V':

              y = r[1]

              break

            case 'M':

              mx = r[r.length - 2]

              my = r[r.length - 1]

              break

            default:

              x = r[r.length - 2]

              y = r[r.length - 1]

          }

        }

      }

      function ellipsePath(x, y, rx, ry, a) {

        if (a == null && ry == null) {

          ry = rx

        }

        x = +x

        y = +y

        rx = +rx

        ry = +ry

        if (a != null) {

          var rad = Math.PI / 180,

            x1 = x + rx * Math.cos(-ry * rad),

            x2 = x + rx * Math.cos(-a * rad),

            y1 = y + rx * Math.sin(-ry * rad),

            y2 = y + rx * Math.sin(-a * rad),

            res = [['M', x1, y1], ['A', rx, rx, 0, +(a - ry > 180), 0, x2, y2]]

        } else {

          res = [

            ['M', x, y],

            ['m', 0, -ry],

            ['a', rx, ry, 0, 1, 1, 0, 2 * ry],

            ['a', rx, ry, 0, 1, 1, 0, -2 * ry],

            ['z']

          ]

        }

        return res

      }

      // http://schepers.cc/getting-to-the-point

      function catmullRom2bezier(crp, z) {

        var d = []

        for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {

          var p = [

            {x: +crp[i - 2], y: +crp[i - 1]},

            {x: +crp[i], y: +crp[i + 1]},

            {x: +crp[i + 2], y: +crp[i + 3]},

            {x: +crp[i + 4], y: +crp[i + 5]}

          ]

          if (z) {

            if (!i) {

              p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]}

            } else if (iLen - 4 == i) {

              p[3] = {x: +crp[0], y: +crp[1]}

            } else if (iLen - 2 == i) {

              p[2] = {x: +crp[0], y: +crp[1]}

              p[3] = {x: +crp[2], y: +crp[3]}

            }

          } else {

            if (iLen - 4 == i) {

              p[3] = p[2]

            } else if (!i) {

              p[0] = {x: +crp[i], y: +crp[i + 1]}

            }

          }

          d.push(['C',

            (-p[0].x + 6 * p[1].x + p[2].x) / 6,

            (-p[0].y + 6 * p[1].y + p[2].y) / 6,

            (p[1].x + 6 * p[2].x - p[3].x) / 6,

            (p[1].y + 6 * p[2].y - p[3].y) / 6,

            p[2].x,

            p[2].y

          ])

        }

        return d

      }

      return res

    }

    function path2curve(path, path2) {

      var p = pathToAbsolute(path),

        attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},

        // attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},

        processPath = function(path, d, pcom) {

          var nx, ny

          if (!path) {

            return ['C', d.x, d.y, d.x, d.y, d.x, d.y]

          }

          !(path[0] in {T: 1, Q: 1}) && (d.qx = d.qy = null)

          switch (path[0]) {

            case 'M':

              d.X = path[1]

              d.Y = path[2]

              break

            case 'A':

              path = ['C'].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1))))

              break

            case 'S':

              if (pcom == 'C' || pcom == 'S') { // In "S" case we have to take into account, if the previous command is C/S.

                nx = d.x * 2 - d.bx // And reflect the previous

                ny = d.y * 2 - d.by // command's control point relative to the current point.

              } else { // or some else or nothing

                nx = d.x

                ny = d.y

              }

              path = ['C', nx, ny].concat(path.slice(1))

              break

            case 'T':

              if (pcom == 'Q' || pcom == 'T') { // In "T" case we have to take into account, if the previous command is Q/T.

                d.qx = d.x * 2 - d.qx // And make a reflection similar

                d.qy = d.y * 2 - d.qy // to case "S".

              } else { // or something else or nothing

                d.qx = d.x

                d.qy = d.y

              }

              path = ['C'].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]))

              break

            case 'Q':

              d.qx = path[1]

              d.qy = path[2]

              path = ['C'].concat(q2c(d.x, d.y, path[1], path[2], path[3], path[4]))

              break

            case 'L':

              path = ['C'].concat(l2c(d.x, d.y, path[1], path[2]))

              break

            case 'H':

              path = ['C'].concat(l2c(d.x, d.y, path[1], d.y))

              break

            case 'V':

              path = ['C'].concat(l2c(d.x, d.y, d.x, path[1]))

              break

            case 'Z':

              path = ['C'].concat(l2c(d.x, d.y, d.X, d.Y))

              break

          }

          function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {

            // for more information of where this math came from visit:

            // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes

            var _120 = Math.PI * 120 / 180,

              rad = Math.PI / 180 * (+angle || 0),

              res = [],

              xy,

              rotate = function(x, y, rad) {

                var X = x * Math.cos(rad) - y * Math.sin(rad),

                  Y = x * Math.sin(rad) + y * Math.cos(rad)

                return {x: X, y: Y}

              }

            if (!rx || !ry) {

              return [x1, y1, x2, y2, x2, y2]

            }

            if (!recursive) {

              xy = rotate(x1, y1, -rad)

              x1 = xy.x

              y1 = xy.y

              xy = rotate(x2, y2, -rad)

              x2 = xy.x

              y2 = xy.y

              var cos = Math.cos(Math.PI / 180 * angle),

                sin = Math.sin(Math.PI / 180 * angle),

                x = (x1 - x2) / 2,

                y = (y1 - y2) / 2

              var h = x * x / (rx * rx) + y * y / (ry * ry)

              if (h > 1) {

                h = Math.sqrt(h)

                rx = h * rx

                ry = h * ry

              }

              var rx2 = rx * rx,

                ry2 = ry * ry,

                k = (large_arc_flag == sweep_flag ? -1 : 1) *

                        Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),

                cx = k * rx * y / ry + (x1 + x2) / 2,

                cy = k * -ry * x / rx + (y1 + y2) / 2,

                f1 = Math.asin(((y1 - cy) / ry).toFixed(9)),

                f2 = Math.asin(((y2 - cy) / ry).toFixed(9))

              f1 = x1 < cx ? Math.PI - f1 : f1

              f2 = x2 < cx ? Math.PI - f2 : f2

              f1 < 0 && (f1 = Math.PI * 2 + f1)

              f2 < 0 && (f2 = Math.PI * 2 + f2)

              if (sweep_flag && f1 > f2) {

                f1 = f1 - Math.PI * 2

              }

              if (!sweep_flag && f2 > f1) {

                f2 = f2 - Math.PI * 2

              }

            } else {

              f1 = recursive[0]

              f2 = recursive[1]

              cx = recursive[2]

              cy = recursive[3]

            }

            var df = f2 - f1

            if (Math.abs(df) > _120) {

              var f2old = f2,

                x2old = x2,

                y2old = y2

              f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1)

              x2 = cx + rx * Math.cos(f2)

              y2 = cy + ry * Math.sin(f2)

              res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy])

            }

            df = f2 - f1

            var c1 = Math.cos(f1),

              s1 = Math.sin(f1),

              c2 = Math.cos(f2),

              s2 = Math.sin(f2),

              t = Math.tan(df / 4),

              hx = 4 / 3 * rx * t,

              hy = 4 / 3 * ry * t,

              m1 = [x1, y1],

              m2 = [x1 + hx * s1, y1 - hy * c1],

              m3 = [x2 + hx * s2, y2 - hy * c2],

              m4 = [x2, y2]

            m2[0] = 2 * m1[0] - m2[0]

            m2[1] = 2 * m1[1] - m2[1]

            if (recursive) {

              return [m2, m3, m4].concat(res)

            } else {

              res = [m2, m3, m4].concat(res).join().split(',')

              var newres = []

              for (var i = 0, ii = res.length; i < ii; i++) {

                newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x

              }

              return newres

            }

          }

          return path

        },

        fixArc = function(pp, i) {

          if (pp[i].length > 7) {

            pp[i].shift()

            var pi = pp[i]

            while (pi.length) {

              pcoms1[i] = 'A' // if created multiple C:s, their original seg is saved

              // p2 && (pcoms2[i] = 'A') // the same as above

              pp.splice(i++, 0, ['C'].concat(pi.splice(0, 6)))

            }

            pp.splice(i, 1)

            ii = Math.max(p.length || 0)

          }

        },

        fixM = function(path1, path2, a1, a2, i) {

          if (path1 && path2 && path1[i][0] == 'M' && path2[i][0] != 'M') {

            path2.splice(i, 0, ['M', a2.x, a2.y])

            a1.bx = 0

            a1.by = 0

            a1.x = path1[i][1]

            a1.y = path1[i][2]

            ii = Math.max(p.length, p2 && p2.length || 0)

          }

        },

        pcoms1 = [], // path commands of original path p

        pcoms2 = [], // path commands of original path p2

        pfirst = '', // temporary holder for original path command

        pcom = '' // holder for previous path command of original path

      for (var i = 0, ii = Math.max(p.length || 0); i < ii; i++) {

        p[i] && (pfirst = p[i][0]) // save current path command

        if (pfirst != 'C') // C is not saved yet, because it may be result of conversion

        {

          pcoms1[i] = pfirst // Save current path command

          i && (pcom = pcoms1[i - 1]) // Get previous path command pcom

        }

        p[i] = processPath(p[i], attrs, pcom) // Previous path command is inputted to processPath

        if (pcoms1[i] != 'A' && pfirst == 'C') {pcoms1[i] = 'C'} // A is the only command

        // which may produce multiple C:s

        // so we have to make sure that C is also C in original path

        fixArc(p, i) // fixArc adds also the right amount of A:s to pcoms1

        // if (p2) { // the same procedures is done to p2

        //  p2[i] && (pfirst = p2[i][0])

        //  if (pfirst != 'C') {

        //    pcoms2[i] = pfirst

        //    i && (pcom = pcoms2[i - 1])

        //  }

        //  p2[i] = processPath(p2[i], attrs2, pcom)

        //  if (pcoms2[i] != 'A' && pfirst == 'C') {

        //    pcoms2[i] = 'C'

        //  }

        //  fixArc(p2, i)

        // }

        fixM(p, p, attrs, attrs, i)

        // fixM(p2, p, attrs2, attrs, i)

        var seg = p[i],

          // seg2 = p2 && p2[i],

          seglen = seg.length

          // seg2len = p2 && seg2.length

        attrs.x = seg[seglen - 2]

        attrs.y = seg[seglen - 1]

        attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x

        attrs.by = parseFloat(seg[seglen - 3]) || attrs.y

        // attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x)

        // attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y)

        // attrs2.x = p2 && seg2[seg2len - 2]

        // attrs2.y = p2 && seg2[seg2len - 1]

      }

      function l2c(x1, y1, x2, y2) {

        return [x1, y1, x2, y2, x2, y2]

      }

      function q2c(x1, y1, ax, ay, x2, y2) {

        var _13 = 1 / 3,

          _23 = 2 / 3

        return [

          _13 * x1 + _23 * ax,

          _13 * y1 + _23 * ay,

          _13 * x2 + _23 * ax,

          _13 * y2 + _23 * ay,

          x2,

          y2

        ]

      }

      return p

    }

    function getLengthFactory(istotal, subpath) {

      return function(path, length, onlystart) {

        // if (path instanceof Element) {

        //  path = path.attr('d')

        // }

        path = path2curve(path)

        var x, y, p, l, sp = '', subpaths = {}, point,

          len = 0

        for (var i = 0, ii = path.length; i < ii; i++) {

          p = path[i]

          if (p[0] == 'M') {

            x = +p[1]

            y = +p[2]

          } else {

            l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6])

            if (len + l > length) {

              if (!istotal && !subpath) {

                point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len)

                return point

              }

            }

            len += l

            x = +p[5]

            y = +p[6]

          }

          sp += p.shift() + p

        }

        subpaths.end = sp

        point = istotal ? len : subpath ? subpaths : findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1)

        function getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) {

          function base3(t, p1, p2, p3, p4) {

            var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4,

              t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3

            return t * t2 - 3 * p1 + 3 * p2

          }

          function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) {

            if (z == null) {

              z = 1

            }

            z = z > 1 ? 1 : z < 0 ? 0 : z

            var z2 = z / 2,

              n = 12,

              Tvalues = [-0.1252, 0.1252, -0.3678, 0.3678, -0.5873, 0.5873, -0.7699, 0.7699, -0.9041, 0.9041, -0.9816, 0.9816],

              Cvalues = [0.2491, 0.2491, 0.2335, 0.2335, 0.2032, 0.2032, 0.1601, 0.1601, 0.1069, 0.1069, 0.0472, 0.0472],

              sum = 0

            for (var i = 0; i < n; i++) {

              var ct = z2 * Tvalues[i] + z2,

                xbase = base3(ct, x1, x2, x3, x4),

                ybase = base3(ct, y1, y2, y3, y4),

                comb = xbase * xbase + ybase * ybase

              sum += Cvalues[i] * Math.sqrt(comb)

            }

            return z2 * sum

          }

          function getTotLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) {

            if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) {

              return

            }

            var t = 1,

              step = t / 2,

              t2 = t - step,

              l,

              e = 0.01

            l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2)

            while (Math.abs(l - ll) > e) {

              step /= 2

              t2 += (l < ll ? 1 : -1) * step

              l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2)

            }

            return t2

          }

          if (length == null) {

            return bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y)

          } else {

            return findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y,

              getTotLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length))

          }

        }

        function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {

          var t1 = 1 - t,

            t13 = Math.pow(t1, 3),

            t12 = Math.pow(t1, 2),

            t2 = t * t,

            t3 = t2 * t,

            x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x,

            y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y,

            mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),

            my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),

            nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),

            ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),

            ax = t1 * p1x + t * c1x,

            ay = t1 * p1y + t * c1y,

            cx = t1 * c2x + t * p2x,

            cy = t1 * c2y + t * p2y,

            alpha = 90 - Math.atan2(mx - nx, my - ny) * 180 / Math.PI

            // (mx > nx || my < ny) && (alpha += 180);

          return {

            x: x,

            y: y,

            m: {x: mx, y: my},

            n: {x: nx, y: ny},

            start: {x: ax, y: ay},

            end: {x: cx, y: cy},

            alpha: alpha

          }

        }

        return point

      }

    }

    var getTotalLength = getLengthFactory(1)

    var getPointAtLength = getLengthFactory()

    // --------------------------------------------------------

    // svgpath转为游戏坐标数组

    // i 间距

    // varianceX 横向调整

    // varianceY 纵向向调整

    function getPointArr(svgPath, i, varianceX, varianceY) {

      var pointArr = []

      var totalLen = getTotalLength(svgPath)

      for(var pathLen = 0;pathLen < totalLen;pathLen += i) {

        var preMove = getPointAtLength(svgPath, pathLen)

        pointArr.push({x: preMove.x * fix + varianceX, y: (414 * fix - preMove.y * fix) + varianceY})

      }

      return pointArr

    }

    参考https://github.com/adobe-webplatform/Snap.svg/blob/master/dist/snap.svg.js

    相关文章

      网友评论

          本文标题:svg转成坐标数组

          本文链接:https://www.haomeiwen.com/subject/fzobpqtx.html