【问题标题】:D3 - Create Dynamic "Border" Rectangle around SVG groupD3 - 在 SVG 组周围创建动态“边框”矩形
【发布时间】:2014-08-01 18:46:16
【问题描述】:

我有一个 SVG 组,其中包含一个矩形,并希望该矩形充当该组的边框...

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

但该组是动态的,其内容会发生变化。我正在尝试在我的更新函数中调整矩形的大小

.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 })

但似乎发生的情况是它扩展得比较好,但由于组的边界框宽度现在与扩展矩形的宽度相同(矩形的宽度是组的宽度,但组的宽度现在是矩形的宽度)。

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

【问题讨论】:

  • 为什么需要rect 成为g 元素的子元素?
  • @Duopixel 这让事情更有条理(因为你会期望边框在其组内,而不是在组外),但你是对的,我不需要它作为一个孩子!如果您将此添加为答案,我会接受它,因为这个明显的解决方案解决了我的问题:)

标签: javascript svg d3.js


【解决方案1】:

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

  • 使用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。

【讨论】:

  • 为什么我从未听说过outline 属性?我在 SO for SVG 边框上看到的每篇文章都涉及 rect 但这要容易得多!我似乎无法让它在 IE9 上运行,尽管 MDN 说它应该与 IE8+ 一起使用:\
【解决方案2】:

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

假设您的边界矩形有一个 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 就是一个例子。

【讨论】:

  • 感谢您的广泛回答!不幸的是,即使经过一些修补,这也是一个相当复杂的解决方案,对我来说不太适用。我在整个组中嵌套了许多组,因此它们的内部元素都相对定位......我尝试使用.getCTM() 并获得翻译,但经过一两个小时的反复试验,它不起作用: \
  • 边界框已经相对于局部坐标。即使您将组嵌套在多个应用了平移和缩放的组中,边界框仍将正确地绑定您的元素。请参阅更新的示例HERE。也许我误解了你的问题。无论如何,您似乎找到了适合您的解决方案,祝您好运。
  • 我找到了解决办法,再次感谢!不过,为了澄清起见,我的意思是我的内部元素嵌套在组中。在您的示例的上下文中,圆圈的父级与矩形不同,因为它们也将嵌套在其他一些组中。因此,当调用 .getBBox() 时,我得到了相对于嵌套内部组的 x 和 y 位置(而不是像您的示例中那样仅相对于 theGroup)。示例:&lt;g&gt;&lt;rect&gt;&lt;/rect&gt;&lt;g transform='translate(50,50)'&gt;&lt;g transform='translate(100,30)'&gt;&lt;circle x='2' y='3'&gt;&lt;/circle&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-26
    • 2020-03-03
    • 1970-01-01
    相关资源
    最近更新 更多