D3 - 在SVG组周围创建动态"边框"矩形

agi*_*ur5 4 javascript svg d3.js

我有一个SVG组,其内部有一个rect,并希望rect作为该组的边界...

<g>
  <rect></rect>
</g>
Run Code Online (Sandbox Code Playgroud)

但该群体是动态的,其内容也在变化.我试图在我的更新功能中调整rect的大小

.attr("x", function(d) { return this.parentNode.getBBox().x })
.attr("y", function(d) { return this.parentNode.getBBox().y })
.attr("width", function(d) { return this.parentNode.getBBox().width })
.attr("height", function(d) { return this.parentNode.getBBox().height })
Run Code Online (Sandbox Code Playgroud)

但似乎发生的事情是它扩展得相对较好,但是由于组的边界框宽度现在与扩展的rect的宽度相同(矩形的宽度是组的宽度,但是组的宽度现在是矩形的宽度),因此无法正确收缩).

有没有办法让SVG组内的矩形正确调整大小并充当边框?

Eri*_*röm 6

解决这个问题的方法不止一种.

  • 使用该outline属性(2014-08-05状态:适用于Chrome和Opera)

    <svg xmlns="http://www.w3.org/2000/svg" width="500px" height="500px">
      <g style="outline: thick solid black; outline-offset: 10px;">
        <circle cx="50" cy="60" r="20" fill="yellow"/>
        <rect x="80" y="80" width="200" height="100" fill="blue"/>
      </g>
    </svg>
    
    Run Code Online (Sandbox Code Playgroud)

    查看实例.

  • 使用a filter来生成边框(2014-08-05状态:在Firefox中有效,但Chrome/Opera有关于feMorphology 的错误,但应该可以通过使用其他过滤器基元来解决这个问题).

    <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
      <defs>
        <filter id="border" x="-5%" y="-5%" width="110%" height="110%">
          <feFlood flood-color="black" result="outer"/>
          <feMorphology operator="erode" radius="2" in="outer" result="inner"/>
          <feComposite in="inner" in2="outer" operator="xor"/>
          <feComposite in2="SourceGraphic"/>
        </filter>
      </defs>
      <g filter="url(#border)">
        <circle cx="50" cy="60" r="20" fill="yellow"/>
        <rect x="80" y="80" width="200" height="100" fill="blue"/>
      </g>
    </svg>
    
    Run Code Online (Sandbox Code Playgroud)

    查看实例.

以上两者都将自动更新为该组具有的任何大小,而无需进行DOM修改.


jsh*_*ley 5

是的,您可以通过选择组中所有不是边界矩形本身的子元素,然后根据子项的各个边界框计算整体边界框来找到新的边界框。

假设您的边界矩形有一类bounding-rect,您可以执行以下操作:

function updateRect() {
  // SELECT ALL CHILD NODES EXCEPT THE BOUNDING RECT
  var allChildNodes = theGroup.selectAll(':not(.bounding-rect)')[0]

  // `x` AND `y` ARE SIMPLY THE MIN VALUES OF ALL CHILD BBOXES
  var x = d3.min(allChildNodes, function(d) {return d.getBBox().x;}),
      y = d3.min(allChildNodes, function(d) {return d.getBBox().y;}),

      // WIDTH AND HEIGHT REQUIRE A BIT OF CALCULATION
      width = d3.max(allChildNodes, function(d) {
        var bb = d.getBBox();
        return (bb.x + bb.width) - x;
      }),

      height = d3.max(allChildNodes, function(d) {
        var bb = d.getBBox();
        return (bb.y + bb.height) - y;
      });

  // UPDATE THE ATTRS FOR THE RECT
  svg.select('.bounding-rect')
     .attr('x', x)
     .attr('y', y)
     .attr('width', width)
     .attr('height', height);
}
Run Code Online (Sandbox Code Playgroud)

这会将整个边界框的 x 和 y 值设置为儿童边界框中的最小 x 和 y 值。然后通过找到最大右边界bb.x + bb.width并减去整个框的x来计算整体宽度。然后以与宽度相同的方式计算总高度。

这里是一个例子。


agi*_*ur5 1

最简单的跨浏览器兼容方法是实现边框,rect就像我一样使用 a ,但将其放置在组之外,正如 @Duopixel 在他的评论中提到的。由于它仍然由边界框定位,因此它将具有正确的宽度、高度、x 和 y。

<rect></rect>
<g></g>
Run Code Online (Sandbox Code Playgroud)