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

Posted

技术标签:

【中文标题】如何根据文本宽度动态调整 SVG 矩形的大小?【英文标题】:How can I dynamically resize an SVG rect based on text width? 【发布时间】:2019-05-17 19:12:06 【问题描述】:

在 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 标签来获得更多帮助 嗯,这不是一个真正的 CSS 问题。我主要需要知道如何设置一些东西,而不是什么 这有帮助吗? ***.com/questions/15500894/… 它帮助我解决了另一个问题。我实际上赞成那里的一个答案。 【参考方案1】:

我会使用getComputedTextLength 来测量文本。我不知道在 D3.js 中是否有等价物我的答案是使用纯 javascript 并假设 rect 和文本中心是 x:50,y:25 并且您使用的是textdominant-baseline:middle;text-anchor:middle;

let text_length = txt.getComputedTextLength();

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

您也可以使用txt.textLength.baseVal.value 代替txt.getComputedTextLength()

【讨论】:

好建议,但不是我真正需要的。【参考方案2】:

当您记得 attr() 调用中的 this 绑定引用关联的 HTML (SVG) 元素时,解决方案非常简单:

rects.attr("width",
    d => this.parentNode.childNodes[1].getComputedTextLength() + 20
);

rect 是组成显示节点的 SVG 元素列表中的第一个元素。该节点的 text 位于索引 1 处(如下来自 append 调用)。

【讨论】:

【参考方案3】:

通常我会发表评论,但我没有足够的声望点。 接受的答案有正确的想法,但它不起作用,他是如何编码的。第一个问题是,他使用箭头函数而不是匿名函数。在箭头函数中,this 具有不同的作用域。所以在这里使用匿名函数。

第二个问题是recttext的顺序,你可以在源代码中看到,在问题中。由于rect 附加在text 之前,因此父节点还没有子text。所以你必须just appendrect,然后追加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,你不必像我一样接受它。

【讨论】:

问题是,这是我自己对我的问题的回答,我发布了它,因为它的工作原理与书面完全一样。箭头函数是必不可少的(您可能应该始终使用它,除非您有特定的理由不这样做),因为它将this 绑定到不同的上下文(取决于调用者),但始终绑定到所有者类/函数(这是你最想要的)。 我试过你的例子,但它没有那样工作。我首先必须应用我的修复程序,所以我为这个案例发布了它,其他的问题和我一样 我无法想象,在你的例子中,dom/javascript 没有抱怨,孩子text 还没有被附加。 另外值得注意的是,d3 本身使用了匿名函数。

以上是关于如何根据文本宽度动态调整 SVG 矩形的大小?的主要内容,如果未能解决你的问题,请参考以下文章

根据容器宽度动态调整文本大小

如何根据标签可用的高度调整 UILabel 字体大小

动态调整单个形状的大小而不缩放整个 SVG

根据内容动态调整 HTML 文本输入宽度

QML滑块调整矩形

如何根据内容动态调整 Twitter Bootstrap 模式的大小