svg转成坐标数组

2019-03-11  本文已影响0人  凡凡的小web

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

上一篇下一篇

猜你喜欢

热点阅读