如何根据文本宽度动态调整SVG矩形的大小?

12

我在创建SVG图形中的节点元素时,包括一个矩形和一些文本。由于文本内容可能相差很大,因此我希望根据文本的宽度设置矩形的宽度。

以下是使用D3.js创建具有固定宽度和高度值的矩形:

var rects = nodeEnter.append("rect")
    .attr("width", rectW)
    .attr("height", rectH);

接下来是文本元素:

var nodeText = nodeEnter.append("text")
    .attr("class", "node-text")
    .attr("y", rectH / 2)
    .attr("dy", ".35em")
    .text(function (d) {
        return d.data.name;
    });
nodeText // The bounding box is valid not before the node addition happened actually.
    .attr("x", function (d) {
        return (rectW - this.getBBox().width) / 2;
    });

如您所见,目前我将文本置于可用空间的中心。然后我尝试根据文本设置矩形的宽度,但我从未同时获得矩形元素和文本HTML元素(以获取getBBox())。以下是我的一次尝试:

rects.attr("width",
        d => this.getBBox().width + 20
    );

但是显然this是错误的,因为它指的是rects而不是文本。

这里的正确方法是什么?


通过在您的问题中添加CSS标签,您可以获得更多帮助。 - Treycos
嗯,这并不是一个CSS问题。我主要需要知道如何设置某些东西,而不是什么是什么。 - Mike Lischke
这个有帮助吗?https://dev59.com/i2Up5IYBdhLWcg3wHkyo - Robert Longson
它帮助我解决了另一个问题。我实际上还点赞了其中的一个答案。 - Mike Lischke
3个回答

11
我会使用getComputedTextLength来测量文本长度。我不知道D3.js中是否有等效的方法。我的答案是使用纯JavaScript,假设矩形和文本中心是{x:50,y:25}并且您正在使用text {dominant-baseline:middle; text-anchor:middle;}

let text_length = txt.getComputedTextLength();

rct.setAttributeNS(null,"width",text_length )
rct.setAttributeNS(null,"x",(50 - text_length/2) )
svg{border:1px solid}
text{dominant-baseline:middle;text-anchor:middle;}
<svg viewBox="0 0 100 50">
  <rect x="25" y="12.5" width="50" height="25" stroke="black" fill="none" id="rct" />
  <text x="50" y="25" id="txt">Test text</text>
</svg>

另外,您可以使用txt.textLength.baseVal.value而不是txt.getComputedTextLength()


1
好的建议,但不是我需要的。 - Mike Lischke

7
解决方案很简单,只需记住attr()调用中的this绑定是指相关的HTML(SVG)元素即可:
rects.attr("width",
    d => this.parentNode.childNodes[1].getComputedTextLength() + 20
);

rect 是组成显示节点的 SVG 元素列表中的第一个元素。该节点的 text 在索引 1 处(如通过 append 调用所示)。


2

通常我会进行评论,但是我没有足够的声望点数。被接受的答案有正确的想法,但他编码时出现了问题。第一个问题是,他使用了箭头函数而不是匿名函数。在箭头函数中,this有不同的作用域。因此,在这里使用匿名函数。

第二个问题是recttext的顺序,正如您在问题的源代码中所看到的那样。由于recttext之前添加,父节点还没有子节点text。所以你需要just append rect,然后附加text并设置其attrs,然后设置rectattrs。所以解决方案是:

var rects = nodeEnter.append("rect")

var nodeText = nodeEnter.append("text")
    .attr("class", "node-text")
    .attr("y", rectH / 2)
    .attr("dy", ".35em")
    .text(function (d) {
        return d.data.name;
    });
nodeText // The bounding box is valid not before the node addition happened actually.
    .attr("x", function (d) {
        return (rectW - this.getBBox().width) / 2;
    });

rect
    .attr('width', function () {
        return this.parentNode.childNodes[1].getComputedTextLength();
    })
    .attr("height", rectH);

注意:如果您不需要参数d,您不必像我一样接受它。

1
问题在于,这是我自己对问题的答案,并且我发布它是因为它完全按照所写的方式工作。箭头函数是必不可少的(除非你有特定的原因不使用),因为它将this绑定到所有者类/函数(这通常是您想要的),而不是根据调用者而变化的上下文。 - Mike Lischke
我尝试了你的示例,但是它并没有按照那种方式工作。首先,我必须应用我的修复措施,所以我为这种情况发布了内容,以便其他人遇到像我一样的问题。 - Rip
我无法想象,在你的例子中,DOM/JavaScript 没有抱怨子元素 text 尚未被附加。 - Rip
值得注意的是,d3本身使用匿名函数。 - Rip

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接