2009-10-28 180 views
83

我正在为SVG文件制作一些ECMAScript/JavaScript,并且需要获取text元素的widthheight,因此我可以调整围绕它的矩形大小。在HTML中,我可以使用元素上的offsetWidthoffsetHeight属性,但看起来这些属性不可用。SVG获取文本元素宽度

这是我需要处理的一个片段。每当我改变文字时,我都需要改变矩形的宽度,但我不知道如何获得text元素的实际width(以像素为单位)。

<rect x="100" y="100" width="100" height="100" /> 
<text>Some Text</text> 

任何想法?

回答

125
var bbox = textElement.getBBox(); 
var width = bbox.width; 
var height = bbox.height; 

然后相应地设置矩形的属性。

链接:getBBox()在SVG v1.1标准中。

+1

谢谢你为我工作。我还发现了另一个可以帮助宽度的功能。 textElement.getComputedTextLength()。今晚我会试一试,看看哪个更适合我。 – spudly

+4

我上周在玩这些游戏, getComputedTextLength()方法返回的结果与getBBox()。width的结果相同,因此我只是使用边界框,因为我需要宽度和高度。我不确定是否有任何情况下文本长度会不同于宽度:我想也许提供这种方法来帮助进行文本布局,例如将文本分成多行,而边界框是可用的通用方法在所有(?)元素上。 – NickFitz

+0

到'getBBox()'的链接已经移动到http://wiki.svg.org/index.php/GetBBox – RobM

5

关于文本的长度,该链接似乎表示BBox,而getComputedTextLength()可能会返回稍微不同的值,但它们之间的距离相当接近。

http://bl.ocks.org/MSCAU/58bba77cdcae42fc2f44

+0

谢谢你的链接!我必须自己完成所有场景,但这是一个非常好的总结......我的问题是在firefox上的Tspan元素上使用getBBox()...它不是一个更具体和烦人的问题。 。 再次感谢! –

2

如何像这样的兼容性:

function svgElemWidth(elem) { 
    var methods = [ // name of function and how to process its result 
     { fn: 'getBBox', w: function(x) { return x.width; }, }, 
     { fn: 'getBoundingClientRect', w: function(x) { return x.width; }, }, 
     { fn: 'getComputedTextLength', w: function(x) { return x; }, }, // text elements only 
    ]; 
    var widths = []; 
    var width, i, method; 
    for (i = 0; i < methods.length; i++) { 
     method = methods[i]; 
     if (typeof elem[method.fn] === 'function') { 
      width = method.w(elem[method.fn]()); 
      if (width !== 0) { 
       widths.push(width); 
      } 
     } 
    } 
    var result; 
    if (widths.length) { 
     result = 0; 
     for (i = 0; i < widths.length; i++) { 
      result += widths[i]; 
     } 
     result /= widths.length; 
    } 
    return result; 
} 

这返回的三种方法中的任何有效结果的平均值。如果元素是文本元素,则可以改进它以抛出异常值或支持getComputedTextLength

警告:正如评论所说,getBoundingClientRect是棘手的。无论是从方法删除或只元素,其中getBoundingClientRect将返回了良好的效果使用,所以没有转动,可能不结垢(?)

+0

getBoundingClientRect在另一个可能不同的坐标系中工作。 –

0

不知道为什么,但上述方法都没有为我工作。我用画布方法取得了一些成功,但我必须应用各种比例因子。即使使用比例因子,Safari,Chrome和Firefox之间的结果仍然不一致。

所以,我试过如下:

  var div = document.createElement('div'); 
 
      div.style.position = 'absolute'; 
 
      div.style.visibility = 'hidden'; 
 
      div.style.height = 'auto'; 
 
      div.style.width = 'auto'; 
 
      div.style.whiteSpace = 'nowrap'; 
 
      div.style.fontFamily = 'YOUR_FONT_GOES_HERE'; 
 
      div.style.fontSize = '100'; 
 
      div.style.border = "1px solid blue"; // for convenience when visible 
 

 
      div.innerHTML = "YOUR STRING"; 
 
      document.body.appendChild(div); 
 
      
 
      var offsetWidth = div.offsetWidth; 
 
      var clientWidth = div.clientWidth; 
 
      
 
      document.body.removeChild(div); 
 
      
 
      return clientWidth;

曾为真棒和超精密,但只有在Firefox浏览器。为Chrome和Safari的营救缩放因素,但没有喜悦。事实证明Safari和Chrome错误与字符串长度或字体大小不成线性关系。

所以,方法二。我并不十分关心暴力方法,但是经过多年的苦苦挣扎之后,我决定尝试一下。我决定为每个可打印字符生成不变的值。通常这会很乏味,但幸运的是Firefox恰好是超级准确的。这是我的两部分的蛮力解:

<body> 
 
     <script> 
 
      
 
      var div = document.createElement('div'); 
 
      div.style.position = 'absolute'; 
 
      div.style.height = 'auto'; 
 
      div.style.width = 'auto'; 
 
      div.style.whiteSpace = 'nowrap'; 
 
      div.style.fontFamily = 'YOUR_FONT'; 
 
      div.style.fontSize = '100';   // large enough for good resolution 
 
      div.style.border = "1px solid blue"; // for visible convenience 
 
      
 
      var character = ""; 
 
      var string = "array = ["; 
 
      for(var i=0; i<127; i++) { 
 
       character = String.fromCharCode(i); 
 
       div.innerHTML = character; 
 
       document.body.appendChild(div); 
 
       
 
       var offsetWidth = div.offsetWidth; 
 
       var clientWidth = div.clientWidth; 
 
       console.log("ASCII: " + i + ", " + character + ", client width: " + div.clientWidth); 
 
       
 
       string = string + div.clientWidth; 
 
       if(i<126) { 
 
        string = string + ", "; 
 
       } 
 

 
       document.body.removeChild(div); 
 
       
 
      } 
 
     
 
      var space_string = "! !"; 
 
      div.innerHTML = space_string; 
 
      document.body.appendChild(div); 
 
      console.log("space width: " + div.clientWidth - space_string.charCodeAt(0)*2); 
 
      document.body.removeChild(div); 
 

 
      string = string + "]"; 
 
      div.innerHTML = string; 
 
      document.body.appendChild(div); 
 
      </script> 
 
    </body>

注:上述段已在Firefox执行以产生值的精确阵列。另外,您必须用控制台日志中的空间宽度值替换数组项32。

我只是复制屏幕上的Firefox文本,并将其粘贴到我的JavaScript代码。既然我有可打印字符长度的数组,我可以实现一个get width函数。下面是代码:

const LCARS_CHAR_SIZE_ARRAY = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 26, 46, 63, 42, 105, 45, 20, 25, 25, 47, 39, 21, 34, 26, 36, 36, 28, 36, 36, 36, 36, 36, 36, 36, 36, 27, 27, 36, 35, 36, 35, 65, 42, 43, 42, 44, 35, 34, 43, 46, 25, 39, 40, 31, 59, 47, 43, 41, 43, 44, 39, 28, 44, 43, 65, 37, 39, 34, 37, 42, 37, 50, 37, 32, 43, 43, 39, 43, 40, 30, 42, 45, 23, 25, 39, 23, 67, 45, 41, 43, 42, 30, 40, 28, 45, 33, 52, 33, 36, 31, 39, 26, 39, 55]; 
 

 

 
    static getTextWidth3(text, fontSize) { 
 
     let width = 0; 
 
     let scaleFactor = fontSize/100; 
 
     
 
     for(let i=0; i<text.length; i++) { 
 
      width = width + LCARS_CHAR_SIZE_ARRAY[text.charCodeAt(i)]; 
 
     } 
 
     
 
     return width * scaleFactor; 
 
    }

嗯,就是这样。蛮力,但它在所有三种浏览器中都超级准确,而我的沮丧程度已经降到零。不知道随着浏览器的发展,它会持续多久,但是对于我来说,为我的SVG文本开发一个强大的字体度量技术应该足够长。

1
document.getElementById('yourTextId').getComputedTextLength();