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

时间:2014-08-01 18:46:17

标签: javascript svg d3.js

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

<g>
  <rect></rect>
</g>

但该群体是动态的,其内容会发生变化。我试图在我的更新功能中调整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 })

但似乎发生的事情是,它扩展得相对较好,但是由于组的边界框宽度现在与扩展的rect的宽度相同,因此无法正常缩小(rect&#39; s width是组的宽度,但组的宽度现在是rect的宽度。

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

3 个答案:

答案 0 :(得分: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>
    

    请参阅live example

  • 使用filter生成边框( 2014-08-05状态:适用于Firefox,但Chrome / Opera在feMorphology上有bug,但应该可以通过使用其他过滤器原语来解决这个问题。)

    <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>
    

    请参阅live example

以上两者都会自动更新为该组的大小,而无需修改DOM。

答案 1 :(得分:4)

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

假设您的边界矩阵有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);
}

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

HERE就是一个例子。

答案 2 :(得分:1)

最简单,跨浏览器兼容的方法是实现边框就像我一样使用rect,但将它放在组外(因为它仍然被边界框定位,它将@Duopixel在评论中提到正确的宽度,高度,x和y。