获取SVG路径的边界框

问题描述 投票:0回答:3

我想要这个页面的功能

http://codepen.io/netsi1964/full/vNoemp/

我有路径,需要知道它的边界框作为矩形元素,它是 x,y,宽度和高度 给定代码

<path d="M147.5 55.8c-5.8-7.2-13.6-14.4-25.5-14.4-8.4 0-15.4 8.2-27 8.2-9 0-13-7.8-23-7.8C51.4 41.8 31 60.4 31 84.5c0 12.8 4.2 32.5 13.6 49.7C51 146.7 59.4 155 69 155c6.7 0 14.7-6.3 24.2-6.3 8.4 0 16.2 5.6 23.8 5.6 18 0 35-23.5 35-39.3 0-.8-.3-1.4-.3-2v-1c-11.8-6.3-18.2-15.7-18.2-29.3 0-11 4.8-20.5 13.6-26.7l.5-.2zm-53-8.8c13.7-4.2 26.3-14.4 26.3-32 0-1.5-.2-3.3-.4-5.3l-.2-.8C106.4 12.6 94 23.4 94 40.3c0 1.6.2 3.6.6 5.8v.8z" style="translate(0px,-212.47488403320312px) scale(1,1)" >

并了解 rect 属性

svg intersection codepen
3个回答
16
投票

使用纯 JavaScript:为您的路径提供一个 ID,并使用

getBBox()
获取其边界框。

var myPathBox = document.getElementById("myPath").getBBox();
console.log(myPathBox);

这是一个演示:

var myPathBox = document.getElementById("myPath").getBBox();
console.log(myPathBox);
<svg width="400" height="400">
	<path id="myPath" d="M147.5 55.8c-5.8-7.2-13.6-14.4-25.5-14.4-8.4 0-15.4 8.2-27 8.2-9 0-13-7.8-23-7.8C51.4 41.8 31 60.4 31 84.5c0 12.8 4.2 32.5 13.6 49.7C51 146.7 59.4 155 69 155c6.7 0 14.7-6.3 24.2-6.3 8.4 0 16.2 5.6 23.8 5.6 18 0 35-23.5 35-39.3 0-.8-.3-1.4-.3-2v-1c-11.8-6.3-18.2-15.7-18.2-29.3 0-11 4.8-20.5 13.6-26.7l.5-.2zm-53-8.8c13.7-4.2 26.3-14.4 26.3-32 0-1.5-.2-3.3-.4-5.3l-.2-.8C106.4 12.6 94 23.4 94 40.3c0 1.6.2 3.6.6 5.8v.8z" style="translate(0px,-212.47488403320312px) scale(1,1)" >
	</svg>


0
投票

getBBox()
要求将您的元素附加到 DOM。

如果您无法使用本机支持的

getBBox()
方法,例如因为您在虚拟 DOM 环境中工作,您还可以根据原始路径数据
d
字符串计算边界框。

要求

  1. 您需要一个路径数据解析器,将
    d
    路径数据属性字符串转换为可计算命令值的数组。我正在使用基于 SVGPathData 接口方法的 w3c 工作草案的独立解析器,但您也可以使用 Jarek Foksa 的 getPathData polyfill
  2. 将这些命令转换为所有绝对命令和“普通”命令(速记/反射命令,如
    h
    v
    s
    t
    需要标准化为其对应的普通命令)
  3. 计算二次和三次贝塞尔命令中极值点的 x/y
    t
    值 – 然后计算这些
    t
    值处的实际点坐标。参见崔西平的回答“计算三次贝塞尔曲线的边界框”
  4. 计算
    A
    弧线命令的 x/y 极值点 - 需要对弧线命令值进行参数化(rx、ry、角度、largearc、sweep、finalX、finalY) - 基于这篇文章 “如何计算轴-椭圆的对齐边界框?”

// wrapper function to calculate boundaries for aeach segment type
function getPathBBox(pathData) {
  let M = {
    x: pathData[0].values[0],
    y: pathData[0].values[1]
  }
  let xArr = [M.x];
  let yArr = [M.y];
  for (let i = 1; i < pathData.length; i++) {
    let com = pathData[i]
    let {
      type,
      values
    } = com;
    let valuesL = values.length;
    let comPrev = pathData[i - 1]
    let valuesPrev = comPrev.values;
    let valuesPrevL = valuesPrev.length;

    if (valuesL) {
      let p0 = {
        x: valuesPrev[valuesPrevL - 2],
        y: valuesPrev[valuesPrevL - 1]
      };
      let p = {
        x: values[valuesL - 2],
        y: values[valuesL - 1]
      };

      // add final on path point
      xArr.push(p.x)
      yArr.push(p.y)

      if (type === 'C' || type === 'Q') {
        let cp1 = {
          x: values[0],
          y: values[1]
        };
        let cp2 = type === 'C' ? {
          x: values[2],
          y: values[3]
        } : cp1;
        let pts = type === 'C' ? [p0, cp1, cp2, p] : [p0, cp1, p];

        let bezierExtremesT = getBezierExtremeT(pts)
        bezierExtremesT.forEach(t => {
          let pt = getPointAtBezierT(pts, t);
          xArr.push(pt.x);
          yArr.push(pt.y);
        })
      } else if (type === 'A') {
        let arcExtremes = getArcExtemes(p0, values)
        arcExtremes.forEach(pt => {
          xArr.push(pt.x);
          yArr.push(pt.y);
        })
      }
    }
  }

  let xMin = Math.min(...xArr)
  let xMax = Math.max(...xArr)
  let yMin = Math.min(...yArr)
  let yMax = Math.max(...yArr)
  let bbox = {
    x: xMin,
    y: yMin,
    width: xMax - xMin,
    height: yMax - yMin
  }
  return bbox
}

// wrapper functions for quadratic or cubic bezier point calculation
function getPointAtBezierT(pts, t) {
  let pt = pts.length === 4 ? getPointAtCubicSegmentT(pts[0], pts[1], pts[2], pts[3], t) : getPointAtQuadraticSegmentT(pts[0], pts[1], pts[2], t)
  return pt
}


function getBezierExtremeT(pts) {
  let tArr = pts.length === 4 ? cubicBezierExtremeT(pts[0], pts[1], pts[2], pts[3]) : quadraticBezierExtremeT(pts[0], pts[1], pts[2]);
  return tArr;
}


/**
 * based on Nikos M.'s answer
 * how-do-you-calculate-the-axis-aligned-bounding-box-of-an-ellipse
 * https://stackoverflow.com/questions/87734/#75031511
 */

function getArcExtemes(p0, values) {
  // compute point on ellipse from angle around ellipse (theta)
  const arc = (theta, cx, cy, rx, ry, alpha) => {
    // theta is angle in radians around arc
    // alpha is angle of rotation of ellipse in radians
    var cos = Math.cos(alpha),
      sin = Math.sin(alpha),
      x = rx * Math.cos(theta),
      y = ry * Math.sin(theta);

    return {
      x: cx + cos * x - sin * y,
      y: cy + sin * x + cos * y
    };
  }


  //parametrize arcto data
  let arcData = svgArcToCenterParam(p0.x, p0.y, values[0], values[1], values[2], values[3], values[4], values[5], values[6]);
  let {
    rx,
    ry,
    pt,
    endAngle,
    deltaAngle
  } = arcData;

  // arc rotation
  let deg = values[2];

  // final on path point
  let p = {
    x: values[5],
    y: values[6]
  }

  // circle/elipse center coordinates
  let [cx, cy] = [pt.x, pt.y];

  // collect extreme points – add end point
  let extremes = [p]

  // rotation to radians
  let alpha = deg * Math.PI / 180;
  let tan = Math.tan(alpha),
    p1, p2, p3, p4, theta;

  // find min/max from zeroes of directional derivative along x and y
  // along x axis
  theta = Math.atan2(-ry * tan, rx);

  let angle1 = theta;
  let angle2 = theta + Math.PI;
  let angle3 = Math.atan2(ry, rx * tan);
  let angle4 = angle3 + Math.PI;


  // get point for this theta
  p1 = arc(angle1, cx, cy, rx, ry, alpha);

  // get anti-symmetric point
  p2 = arc(angle2, cx, cy, rx, ry, alpha);

  // get point for this theta
  p3 = arc(angle3, cx, cy, rx, ry, alpha);

  // get anti-symmetric point
  p4 = arc(angle4, cx, cy, rx, ry, alpha);


  // inner bounding box
  let xArr = [p0.x, p.x]
  let yArr = [p0.y, p.y]
  let xMin = Math.min(...xArr)
  let xMax = Math.max(...xArr)
  let yMin = Math.min(...yArr)
  let yMax = Math.max(...yArr)


  // on path point close after start
  let angleAfterStart = endAngle - deltaAngle * 0.001
  let pP2 = arc(angleAfterStart, cx, cy, rx, ry, alpha);

  // on path point close before end
  let angleBeforeEnd = endAngle - deltaAngle * 0.999
  let pP3 = arc(angleBeforeEnd, cx, cy, rx, ry, alpha);


  /**
   * expected extremes
   * if leaving inner bounding box
   * (between segment start and end point)
   * otherwise exclude elliptic extreme points
   */

  // left
  if (pP2.x < xMin || pP3.x < xMin) {
    extremes.push(p2)
  }

  // top
  if (pP2.y < yMin || pP3.y < yMin) {
    extremes.push(p4)
  }

  // right
  if (pP2.x > xMax || pP3.x > xMax) {
    extremes.push(p1)
  }

  // bottom
  if (pP2.y > yMax || pP3.y > yMax) {
    extremes.push(p3)
  }

  return extremes;
}


// wrapper functions for quadratic or cubic bezier point calculation
function getPointAtBezierT(pts, t) {
  let pt = pts.length === 4 ? getPointAtCubicSegmentT(pts[0], pts[1], pts[2], pts[3], t) : getPointAtQuadraticSegmentT(pts[0], pts[1], pts[2], t)
  return pt
}

function getBezierExtremeT(pts) {
  let tArr = pts.length === 4 ? cubicBezierExtremeT(pts[0], pts[1], pts[2], pts[3]) : quadraticBezierExtremeT(pts[0], pts[1], pts[2]);
  return tArr;
}


// cubic bezier
function cubicBezierExtremeT(p0, cp1, cp2, p) {
  let [x0, y0, x1, y1, x2, y2, x3, y3] = [p0.x, p0.y, cp1.x, cp1.y, cp2.x, cp2.y, p.x, p.y];
  let extemeT = [],
    a, b, c, t, t1, t2, b2ac, sqrt_b2ac;
  for (let i = 0; i < 2; ++i) {
    if (i == 0) {
      b = 6 * x0 - 12 * x1 + 6 * x2;
      a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
      c = 3 * x1 - 3 * x0;
    } else {
      b = 6 * y0 - 12 * y1 + 6 * y2;
      a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
      c = 3 * y1 - 3 * y0;
    }
    if (Math.abs(a) < 1e-12) {
      if (Math.abs(b) < 1e-12) {
        continue;
      }
      t = -c / b;
      if (0 < t && t < 1) {
        extemeT.push(t);
      }
      continue;
    }
    b2ac = b * b - 4 * c * a;
    if (b2ac < 0) {
      if (Math.abs(b2ac) < 1e-12) {
        t = -b / (2 * a);
        if (0 < t && t < 1) {
          extemeT.push(t);
        }
      }
      continue;
    }
    sqrt_b2ac = Math.sqrt(b2ac);
    t1 = (-b + sqrt_b2ac) / (2 * a);
    if (0 < t1 && t1 < 1) {
      extemeT.push(t1);
    }
    t2 = (-b - sqrt_b2ac) / (2 * a);
    if (0 < t2 && t2 < 1) {
      extemeT.push(t2);
    }
  }

  var j = extemeT.length;
  while (j--) {
    t = extemeT[j];
  }
  return extemeT;

}

// quadratic bezier.
function quadraticBezierExtremeT(p0, cp1, p) {
  let [x0, y0, x1, y1, x2, y2] = [p0.x, p0.y, cp1.x, cp1.y, p.x, p.y];
  let extemeT = [];

  for (var i = 0; i < 2; ++i) {
    a = i == 0 ? x0 - 2 * x1 + x2 : y0 - 2 * y1 + y2;
    b = i == 0 ? -2 * x0 + 2 * x1 : -2 * y0 + 2 * y1;
    c = i == 0 ? x0 : y0;
    if (Math.abs(a) > 1e-12) {
      t = -b / (2 * a);
      if (t > 0 && t < 1) {
        extemeT.push(t);
      }
    }
  }
  return extemeT
}


/**
 * parse pathData from d attribute
 * the core function to parse the pathData array from a d string
 **/
function parseDtoPathData(d) {
  let dClean = d
    // remove new lines and tabs
    .replace(/[\n\r\t]/g, "")
    // replace comma with space
    .replace(/,/g, " ")
    // add space before minus sign
    .replace(/(\d+)(\-)/g, "$1 $2")
    // decompose multiple adjacent decimal delimiters like 0.5.5.5 => 0.5 0.5 0.5
    .replace(/(\.)(?=(\d+\.\d+)+)(\d+)/g, "$1$3 ")
    // add new lines before valid command letters
    .replace(/([mlcsqtahvz])/gi, "\n$1 ")
    // remove duplicate whitespace
    .replace(/\ {2,}/g, " ")
    // remove whitespace from right and left
    .trim();

  // split commands
  let commands = dClean.split("\n").map((val) => {
    return val.trim();
  });

  // compile pathData
  let pathData = [];
  let comLengths = {
    m: 2,
    a: 7,
    c: 6,
    h: 1,
    l: 2,
    q: 4,
    s: 4,
    t: 2,
    v: 1,
    z: 0
  };
  let errors = [];

  // normalize convatenated larceArc and sweep flags
  const unravelArcValues = (values) => {
    let chunksize = 7,
      n = 0,
      arcComs = []
    for (let i = 0; i < values.length; i++) {
      let com = values[i]

      // reset counter
      if (n >= chunksize) {
        n = 0
      }
      // if 3. or 4. parameter longer than 1
      if ((n === 3 || n === 4) && com.length > 1) {

        let largeArc = n === 3 ? com.substring(0, 1) : ''
        let sweep = n === 3 ? com.substring(1, 2) : com.substring(0, 1)
        let finalX = n === 3 ? com.substring(2) : com.substring(1)
        let comN = [largeArc, sweep, finalX].filter(Boolean)
        arcComs.push(comN)
        n += comN.length

      } else {
        // regular
        arcComs.push(com)
        n++
      }
    }
    return arcComs.flat().filter(Boolean);
  }

  for (let i = 0; i < commands.length; i++) {
    let com = commands[i].split(" ");
    let type = com.shift();
    let typeRel = type.toLowerCase();
    let isRel = type === typeRel;

    /**
     * large arc and sweep flags
     * are boolean and can be concatenated like
     * 11 or 01
     * or be concatenated with the final on path points like
     * 1110 10 => 1 1 10 10
     */
    if (typeRel === "a") {
      com = unravelArcValues(com)
    }

    // convert to numbers
    let values = com.map((val) => {
      return parseFloat(val);
    });

    // if string contains repeated shorthand commands - split them
    let chunkSize = comLengths[typeRel];
    let chunk = values.slice(0, chunkSize);
    pathData.push({
      type: type,
      values: chunk
    });

    // too few values
    if (chunk.length < chunkSize) {
      errors.push(
        `${i}. command (${type}) has ${chunk.length}/${chunkSize} values - ${chunkSize - chunk.length} too few`
      );
    }

    // has implicit commands
    if (values.length > chunkSize) {
      let typeImplicit = typeRel === "m" ? (isRel ? "l" : "L") : type;
      for (let i = chunkSize; i < values.length; i += chunkSize) {
        let chunk = values.slice(i, i + chunkSize);
        pathData.push({
          type: typeImplicit,
          values: chunk
        });
        if (chunk.length !== chunkSize) {
          errors.push(
            `${i}. command (${type}) has ${chunk.length + chunkSize}/${chunkSize} - ${chunk.length} values too many `
          );
        }
      }
    }
  }
  if (errors.length) {
    console.log(errors);
  }

  /**
   * first M is always absolute/uppercase -
   * unless it adds relative linetos
   * (facilitates d concatenating)
   */
  pathData[0].type = 'M'
  return pathData;
}


/**
 * converts all commands to absolute
 */

function pathDataToLonghands(pathData) {
  let pathDataAbs = [];
  let offX = pathData[0].values[0];
  let offY = pathData[0].values[1];
  let lastX = offX;
  let lastY = offY;

  // analyze pathdata
  let commandTokens = pathData.map(com => {
    return com.type
  }).join('')
  let hasShorthands = /[hstv]/gi.test(commandTokens);


  pathData.forEach((com, i) => {
    let {
      type,
      values
    } = com;
    let typeRel = type.toLowerCase();
    let typeAbs = type.toUpperCase();
    let valuesL = values.length;
    let isRelative = type === typeRel;
    let comPrev = i > 0 ? pathData[i - 1] : pathData[0];
    let valuesPrev = comPrev.values;
    let valuesPrevL = valuesPrev.length;

    if (isRelative) {
      com.type = typeAbs;

      switch (typeRel) {
        case "a":
          com.values = [
            values[0],
            values[1],
            values[2],
            values[3],
            values[4],
            values[5] + offX,
            values[6] + offY
          ];
          break;


        case "h":
        case "v":
          com.values = type === 'h' ? [values[0] + offX] : [values[0] + offY];
          break;


        case 'm':
        case 'l':
        case 't':
          com.values = [values[0] + offX, values[1] + offY]
          break;

        case "c":
          com.values = [
            values[0] + offX,
            values[1] + offY,
            values[2] + offX,
            values[3] + offY,
            values[4] + offX,
            values[5] + offY
          ];
          break;

        case "q":
        case "s":
          com.values = [
            values[0] + offX,
            values[1] + offY,
            values[2] + offX,
            values[3] + offY,
          ];
          break;
      }
    }
    // is absolute
    else {
      offX = 0;
      offY = 0;
    }

    /**
     * convert shorthands
     */
    if (hasShorthands) {
      let cp1X, cp1Y, cpN1X, cpN1Y, cp2X, cp2Y;
      if (com.type === 'H' || com.type === 'V') {
        com.values = com.type === 'H' ? [com.values[0], lastY] : [lastX, com.values[0]];
        com.type = 'L';
      } else if (com.type === 'T' || com.type === 'S') {

        [cp1X, cp1Y] = [valuesPrev[0], valuesPrev[1]];
        [cp2X, cp2Y] = valuesPrevL > 2 ? [valuesPrev[2], valuesPrev[3]] : [valuesPrev[0], valuesPrev[1]];

        // new control point
        cpN1X = com.type === 'T' ? lastX + (lastX - cp1X) : 2 * lastX - cp2X;
        cpN1Y = com.type === 'T' ? lastY + (lastY - cp1Y) : 2 * lastY - cp2Y;

        com.values = [cpN1X, cpN1Y, com.values].flat();
        com.type = com.type === 'T' ? 'Q' : 'C';
      }
    }

    // add command
    pathDataAbs.push(com)

    // update offsets
    lastX = valuesL > 1 ? values[valuesL - 2] + offX : (typeRel === 'h' ? values[0] + offX : lastX);
    lastY = valuesL > 1 ? values[valuesL - 1] + offY : (typeRel === 'v' ? values[0] + offY : lastY);
    offX = lastX;
    offY = lastY;
  });

  return pathDataAbs;
}


/**
 * Linear  interpolation (LERP) helper
 */
function interpolatedPoint(p1, p2, t = 0.5) {
  return {
    x: (p2.x - p1.x) * t + p1.x,
    y: (p2.y - p1.y) * t + p1.y
  };
}

/**
 * calculate single points on segments
 */
function getPointAtCubicSegmentT(p0, cp1, cp2, p, t = 0.5) {
  let t1 = 1 - t;
  return {
    x: t1 ** 3 * p0.x +
      3 * t1 ** 2 * t * cp1.x +
      3 * t1 * t ** 2 * cp2.x +
      t ** 3 * p.x,
    y: t1 ** 3 * p0.y +
      3 * t1 ** 2 * t * cp1.y +
      3 * t1 * t ** 2 * cp2.y +
      t ** 3 * p.y
  };
}

function getPointAtQuadraticSegmentT(p0, cp1, p, t = 0.5) {
  let t1 = 1 - t;
  return {
    x: t1 * t1 * p0.x + 2 * t1 * t * cp1.x + t ** 2 * p.x,
    y: t1 * t1 * p0.y + 2 * t1 * t * cp1.y + t ** 2 * p.y
  };
}



/**
 * based on @cuixiping;
 * https://stackoverflow.com/questions/9017100/calculate-center-of-svg-arc/12329083#12329083
 */
function svgArcToCenterParam(x1, y1, rx, ry, degree, fA, fS, x2, y2) {
  const radian = (ux, uy, vx, vy) => {
    let dot = ux * vx + uy * vy;
    let mod = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
    let rad = Math.acos(dot / mod);
    if (ux * vy - uy * vx < 0) {
      rad = -rad;
    }
    return rad;
  };
  // degree to radian
  let phi = (degree * Math.PI) / 180;
  let cx, cy, startAngle, deltaAngle, endAngle;
  let PI = Math.PI;
  let PIx2 = PI * 2;
  if (rx < 0) {
    rx = -rx;
  }
  if (ry < 0) {
    ry = -ry;
  }
  if (rx == 0 || ry == 0) {
    // invalid arguments
    throw Error("rx and ry can not be 0");
  }
  let s_phi = Math.sin(phi);
  let c_phi = Math.cos(phi);
  let hd_x = (x1 - x2) / 2; // half diff of x
  let hd_y = (y1 - y2) / 2; // half diff of y
  let hs_x = (x1 + x2) / 2; // half sum of x
  let hs_y = (y1 + y2) / 2; // half sum of y
  // F6.5.1
  let x1_ = c_phi * hd_x + s_phi * hd_y;
  let y1_ = c_phi * hd_y - s_phi * hd_x;
  // F.6.6 Correction of out-of-range radii
  //   Step 3: Ensure radii are large enough
  let lambda = (x1_ * x1_) / (rx * rx) + (y1_ * y1_) / (ry * ry);
  if (lambda > 1) {
    rx = rx * Math.sqrt(lambda);
    ry = ry * Math.sqrt(lambda);
  }
  let rxry = rx * ry;
  let rxy1_ = rx * y1_;
  let ryx1_ = ry * x1_;
  let sum_of_sq = rxy1_ * rxy1_ + ryx1_ * ryx1_; // sum of square
  if (!sum_of_sq) {
    throw Error("start point can not be same as end point");
  }
  let coe = Math.sqrt(Math.abs((rxry * rxry - sum_of_sq) / sum_of_sq));
  if (fA == fS) {
    coe = -coe;
  }
  // F6.5.2
  let cx_ = (coe * rxy1_) / ry;
  let cy_ = (-coe * ryx1_) / rx;
  // F6.5.3
  cx = c_phi * cx_ - s_phi * cy_ + hs_x;
  cy = s_phi * cx_ + c_phi * cy_ + hs_y;
  let xcr1 = (x1_ - cx_) / rx;
  let xcr2 = (x1_ + cx_) / rx;
  let ycr1 = (y1_ - cy_) / ry;
  let ycr2 = (y1_ + cy_) / ry;
  // F6.5.5
  startAngle = radian(1.0, 0, xcr1, ycr1);
  // F6.5.6
  deltaAngle = radian(xcr1, ycr1, -xcr2, -ycr2);
  while (deltaAngle > PIx2) {
    deltaAngle -= PIx2;
  }
  while (deltaAngle < 0) {
    deltaAngle += PIx2;
  }
  if (fS == false || fS == 0) {
    deltaAngle -= PIx2;
  }
  endAngle = startAngle + deltaAngle;
  while (endAngle > PIx2) {
    endAngle -= PIx2;
  }
  while (endAngle < 0) {
    endAngle += PIx2;
  }
  let toDegFactor = 180 / PI;
  let outputObj = {
    pt: {
      x: cx,
      y: cy
    },
    rx: rx,
    ry: ry,
    startAngle_deg: startAngle * toDegFactor,
    startAngle: startAngle,
    deltaAngle_deg: deltaAngle * toDegFactor,
    deltaAngle: deltaAngle,
    endAngle_deg: endAngle * toDegFactor,
    endAngle: endAngle,
    clockwise: fS == true || fS == 1
  };
  return outputObj;
}
<svg id="svg" width="400" height="400">
  <path id="path" d="M147.5 55.8c-5.8-7.2-13.6-14.4-25.5-14.4-8.4 0-15.4 8.2-27 8.2-9 0-13-7.8-23-7.8C51.4 41.8 31 60.4 31 84.5c0 12.8 4.2 32.5 13.6 49.7C51 146.7 59.4 155 69 155c6.7 0 14.7-6.3 24.2-6.3 8.4 0 16.2 5.6 23.8 5.6 18 0 35-23.5 35-39.3 0-.8-.3-1.4-.3-2v-1c-11.8-6.3-18.2-15.7-18.2-29.3 0-11 4.8-20.5 13.6-26.7l.5-.2zm-53-8.8c13.7-4.2 26.3-14.4 26.3-32 0-1.5-.2-3.3-.4-5.3l-.2-.8C106.4 12.6 94 23.4 94 40.3c0 1.6.2 3.6.6 5.8v.8z">
</svg>

<script>
  window.addEventListener('DOMContentLoaded', e => {
    let d = path.getAttribute('d')
    let pathData = parseDtoPathData(d)

    // normalize to all absolute and longhands
    pathData = pathDataToLonghands(pathData)

    // calculate bounding box
    let bbPath = getPathBBox(pathData);

    // render bounding box
    let {
      x,
      y,
      width,
      height
    } = bbPath;
    let rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
    rect.setAttribute('x', x)
    rect.setAttribute('y', y)
    rect.setAttribute('width', width)
    rect.setAttribute('height', height)
    rect.setAttribute('fill', 'none')
    rect.setAttribute('stroke', 'red')
    svg.append(rect)
  })
</script>


-6
投票

我知道这是一个老问题,但我想我会把这个变体放到 Furtado 的答案中以供参考。

获取路径边界框的简单方法。

  1. 确保路径(或任何其他 SVG 元素上有一个 id。
  2. 在 Chrome(或 FF 或可能 IE)中打开 svg 文件。
  3. 检查图像
  4. 打开检查工具中的控制台。
  5. 输入JS:document.getElementById("myPath").getBBox(); (在哪里 myPath 是 id)

边界框信息将显示在控制台中。

同样的方法,只是不需要自定义代码。

© www.soinside.com 2019 - 2024. All rights reserved.