【问题标题】:Get the bounding box of the intersection of 2 or more paths获取2条或多条路径交集的边界框
【发布时间】:2020-07-07 16:34:15
【问题描述】:

在下面的例子中,我有 3 个形状的交点(在本例中,我使用的是圆形,但这 3 个形状可以是任何形状)金色的交点是使用剪辑路径剪辑的结果。

我想使用交叉点作为符号,为此我需要知道交叉点的边界框,即红色描边矩形。

如果我使用 intersection.getBBox(),我会在剪切之前获取边界框。

如何获取路口的边界框?

console.log(intersection.getBBox())
svg{border:solid}

.circles{fill:none;stroke:black}
<svg id="svg" viewBox="-150 -150 300 300" width="300">
  <defs>
  <circle id="c1" cx="0" cy="-50" r="80"></circle>
  <circle id="c2" cx="43.3" cy="25" r="80"></circle>
  <circle id="c3" cx="-43.3" cy="25" r="80"></circle>
  
  <clipPath id="clipC2"><use xlink:href="#c2"/></clipPath>
  <clipPath id="clipC3"><use xlink:href="#c3"/></clipPath>
  </defs>
  
  <g class="circles">
  <use xlink:href="#c1"/>
  <use xlink:href="#c2"/>
  <use xlink:href="#c3"/>
  </g>
  
  
<g id="intersection">
  <g clip-path="url(#clipC3)">
  <use fill="gold" xlink:href="#c1" clip-path="url(#clipC2)"/>
  </g>
</g>
  
  <rect x="-38" y="-42" width="75" height="74" stroke="red" fill="none"/>
</svg>

【问题讨论】:

    标签: javascript svg


    【解决方案1】:

    主要思想是这样的:

    1. 我正在使用 svg 元素,将其设为 base64 并将其用作图像的 src 属性。
    2. 我在 canvas 上绘制 svg 元素,其大小与 svg 元素相同。
    3. 我从画布中获取图像数据
    4. 遍历图像数据并获取:
    • 黑色像素的最小 x 值
    • 黑色像素的最小 y 值
    • 黑色像素的最大 x 值
    • 黑色像素的最大 y 值
    1. 我正在使用这些值来为交叉点构建新的 viewBox 值。

    //the svg's viewBox
    let vB = { x: -100, y: -100, w: 200, h: 200 };
    
    //canvas
    let ctx = c.getContext("2d");
    //set the size of the canvas equal to the size of the svg element
    c.width = vB.w;
    c.height = vB.h;
    
    // draw the svg element on the canvas
    let xml = new XMLSerializer().serializeToString(svg);
    // make it base64 and use it as the src attribute of the image
    let img=new Image()
    img.src = "data:image/svg+xml;base64," + btoa(xml);
    img.onload = function() {
      //paint the image on the canvas
        ctx.drawImage(this, 0, 0);
      
    //get the image data from the canvas
    let imgData = ctx.getImageData(0, 0, vB.w, vB.h).data;
    
    // x the smallest x value of a black pixel
    // y the smallest y value of a black pixel
    // X the biggest x value of a black pixel
    // Y the biggest y value of a black pixel
    let x = vB.w,
      y = vB.h,
      X = 0,
      Y = 0;
    let n = 0;
    
    for (let i = 0; i < imgData.length; i += 4) {
      n++
      if (imgData[i + 3] != 0) {
        //if the alpha (i+3) value of the pixel is not 0
        let _y = Math.ceil(i / (4 * vB.w));
        let _x = (i / 4) % vB.w;
        if (_x < x) { x = _x; }
        if (_y < y) { y = _y; }
        if (_x > X) { X = _x; }
        if (_y > Y) { Y = _y; }
      } 
      
      if(n==imgData.length/4){
        let newViewBox = `${x + vB.x} ${y + vB.y} ${X - x + 1} ${Y - y}`;
        reuleaux.setAttribute("viewBox", newViewBox);
        console.log(`viewBox="${newViewBox}"`);
      }
    }
    }
    svg,
    canvas {
      outline: 1px solid;
    }
    <svg id="svg" viewBox="-100 -100 200 200" width="200">
      <defs>
      <circle id="c1" cx="0" cy="-50" r="80"></circle>
      <circle id="c2" cx="43.3" cy="25" r="80"></circle>
      <circle id="c3" cx="-43.3" cy="25" r="80"></circle>  
      <clipPath id="clipC2"><use xlink:href="#c2"/></clipPath>
      <clipPath id="clipC3"><use xlink:href="#c3"/></clipPath>
      </defs>    
    <g id="intersection">
      <g clip-path="url(#clipC3)">
      <use xlink:href="#c1" clip-path="url(#clipC2)"/>
      </g>
    </g>
    </svg>
    <!--<img id="img" width="200" height="200"/>-->
    
    <canvas id="c"></canvas>
    
    <svg id="reuleaux" viewBox="-100 -100 200 200" width="200" style="background:#dfdfdf">
      <use xlink:href="#intersection"/>
    </svg>

    【讨论】:

      【解决方案2】:

      不确定这是否是您所追求的类型。

      let myBB = {
          x: c2.getBBox().x,
          get y() {
              return c1.getBBox().y + this.width
          },
          get width() {
              // return (posNum(c3.getBBox().x))  - (posNum(myBB.x));
              let leftPointOfWidth = c2.getBBox().x;
              let rightPointofWidth = c3.getBBox().x + c3.getBBox().width;
              // 10000 to guarantee both positive numbers. very hacky
              let mywidth = (rightPointofWidth + 10000) - (leftPointOfWidth + 10000);
              return mywidth;
          },
          get height() {
              return this.width;
          }
      }
      

      我很确定有更好的表达方式。并且需要调用吸气剂;它们不会出现在console.log(myBB)

      输入获得的坐标和宽度会得到黄色的矩形。 (粉红色的矩形是圆心)

      【讨论】:

      • 感谢您的回答。不幸的是,除了您做出无端假设之外,myBB 与 3 个圆圈的交点不匹配
      • 所以你确实想要一个console.log(intersection.getBBox()) 并且可能改变DOM?我是真的在说,因为 &lt;g id="intersection"&gt;&lt;/g&gt; 在我看来不太合适,恕我直言,但我无法放置它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-28
      • 1970-01-01
      • 2014-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-08
      相关资源
      最近更新 更多