如何确定SVG文本框的宽度,或在“x”个字符后强制换行?

29

我正在使用 Raphael 库创建一个 SVG 文本框,并将其中填充一个从 XML 文档中提取的动态字符串。

有时,这个字符串比我放置文本框的画布还要长,因此我需要限制盒子的宽度以强制换行(我找不到任何证据表明这是可能的),或者确保在一定数量的字符之后插入 '\n' 换行符。

所以,(1) 这是最好的选择吗? (2) 我该如何做到这一点?

5个回答

47

虽然没有文本换行的属性,但是有一个简单的技巧可以使用。将一个单词一个单词地添加到文本对象中,当它变得太长时,添加一个换行符。您可以使用getBBox()函数确定宽度。基本上,您模拟了一台老式打字机。这是一些代码示例,可以为您完成此操作。您可以轻松地将其转换为接受文本和宽度的简单函数。

var r = Raphael(500, 500);
var t = r.text(100, 100).attr('text-anchor', 'start');
var maxWidth = 100;

var content = "Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate. ";
var words = content.split(" ");

var tempText = "";
for (var i=0; i<words.length; i++) {
  t.attr("text", tempText + " " + words[i]);
  if (t.getBBox().width > maxWidth) {
    tempText += "\n" + words[i];
  } else {
    tempText += " " + words[i];
  }
}

t.attr("text", tempText.substring(1));

我写了一个库,基本上就是做这个的,名为raphael-paragraph。请参见下面的答案。 - Jimmy Breck-McKye

9
谢谢你的回答。然而,我发现需要对它进行一些调整才能适合我:
function textWrap(t, width) {
    var content = t.attr("text");
    var abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    t.attr({
      'text-anchor' : 'start',
      "text" : abc
    });
    var letterWidth = t.getBBox().width / abc.length;
    t.attr({
        "text" : content
    });

    var words = content.split(" ");
    var x = 0, s = [];
    for ( var i = 0; i < words.length; i++) {

        var l = words[i].length;
        if (x + (l * letterWidth) > width) {
            s.push("\n");
            x = 0;
        }
        x += l * letterWidth;
        s.push(words[i] + " ");
    }
    t.attr({
        "text" : s.join("")
    });
}

修改内容如下:
  • 修改比较方式应该使用(l * letterwidth),而不仅仅是 l
  • if/else 语句改为只用 if,这样换行符将始终将 X 设置为0
  • 将新的 l * letterwidth 始终加到 x 值上
希望对你有所帮助。

1
谢谢您提供的代码 - 尽管我不确定为什么需要在顶部加入此代码:'text-anchor': 'start'。我将其删除后,似乎一切正常,并保留了我的原始格式。 - rwalter
1
+1 这是对Cancerbero代码的改进,如果性能是一个因素,那么这是该页面上最佳的选项。一种可能的改进:每种字体和字号的字母宽度都是恒定的,因此,如果您要在大量文本上运行此代码,则可以通过将字母宽度存储在适当作用域的变量中来获得适度的性能提升,而不是针对每个元素使用text attr切换重新计算它。我建议将其存储在对象中,以字体族和字号为键。 - user56reinstatemonica8

3

对于大量文本,Mark的解决方案速度较慢(Firefox 11)。我认为这是因为为了获取BBOX而多次重新呈现文本。以下函数对于大量文本来说更加高效,但或许不够精准(代码来源于raphaelmarkup项目):

/**
 * @param t a raphael text shape
 * @param width - pixels to wrapp text width
 * modify t text adding new lines characters for wrapping it to given width.
 */
rm._textWrapp = function(t, width) {
    var content = t.attr("text");
    var abc="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    t.attr({'text-anchor': 'start', "text": abc});
    var letterWidth=t.getBBox().width / abc.length;
    t.attr({"text": content});
    var words = content.split(" "), x=0, s=[];
    for ( var i = 0; i < words.length; i++) {
        var l = words[i].length;
        if(x+l>width) {
            s.push("\n")
            x=0;
        }
        else {
            x+=l*letterWidth;
        }
        s.push(words[i]+" ");
    }
    t.attr({"text": s.join("")});
};

不错,但是 if(x+l>width) { 看起来像是一个错误 - 它将每个测试中最后一个单词的平均字宽视为1像素。如果你失去了 else 子句并在每个 for 循环的开头放置 x+=l*letterWidth;,它会工作得更好。此外,'text-anchor': 'start', 是不必要的 - 没有必要强制左对齐。编辑 刚刚看到 Evan 的答案考虑了这些问题,并针对此代码做出了回应,而不是 Mark 的答案。 - user56reinstatemonica8

0

嗯,我稍微调整了一下就解决了

var words = server.split( " " );
var length = words.length;
var temp_text = "";

for( var i = 0; i < length; i++ ) {
    temp_text = temp_text + ' ' + words[i];
    t.attr( "text", temp_text );

    if( t.getBBox().width > width ) {
        temp_text = temp_text.replace(/( *)(\w+)$/, "\n$2");
    }
}

t.attr( "text", temp_text.trim() );

0

我知道现在有点晚了,但你可能会对我的Raphael-paragraph项目感兴趣,它可以自动完成这个任务。

Raphael-paragraph允许您创建自动换行的多行文本,并设置最大宽度和高度限制、行高和文本样式配置。它可以断字长单词并截断超出垂直边界的部分。它仍然处于测试阶段,需要进行很多优化,但应该能够满足您的需求。

使用示例和文档可在GitHub页面上找到。


你能提供关于如何使用你的库的详细信息吗?在脚本标签中应该导入哪个文件,调用哪个方法来进行文本换行? - Masu
@Masu 要么将 dist/paragraph.js 文件复制到您的项目中,要么运行 npm install raphael-paragraph 并且通过 import rp from 'raphael-paragraph/src/paragraph' 引入。 - Jimmy Breck-McKye

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