2009-10-28 3 views
83

Я работаю над некоторыми ECMAScript/JavaScript для SVG-файла и должен получить width и height элемента text, чтобы изменить размер прямоугольника, который его окружает. В HTML я мог бы использовать атрибуты offsetWidth и offsetHeight для элемента, но кажется, что эти свойства недоступны.SVG получить ширину текстового элемента

Вот фрагмент, с которым мне нужно работать. Мне нужно изменить ширину прямоугольника всякий раз, когда я меняю текст, но я не знаю, как получить фактический width (в пикселях) элемента text.

<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; 

и затем соответствующим образом установить атрибуты rect.

Ссылка: 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

Спасибо за эту ссылку! Мне пришлось пройти все сценарии самостоятельно, но это действительно хорошее резюме ... Моя проблема заключалась в использовании getBBox() на элементе Tspan на firefox ... Это не могло быть более конкретной и раздражающей проблемой. . еще раз спасибо! –

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; 
} 

Это возвращает среднее арифметическое любых действительных результатов трех методов. Вы можете улучшить его, чтобы изгнать outliers или поддержать 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(); 

работал для меня в

Смежные вопросы