SVG получить ширину текстового элемента
Я работаю над некоторым ECMAScript/JavaScript для файла SVG и мне нужно получить width
а также height
из text
элемент, чтобы я мог изменить размер прямоугольника, который его окружает. В HTML я мог бы использовать offsetWidth
а также offsetHeight
атрибуты элемента, но кажется, что эти свойства недоступны.
Вот фрагмент, с которым мне нужно работать. Мне нужно изменить ширину прямоугольника всякий раз, когда я изменяю текст, но я не знаю, как получить фактический width
(в пикселях) text
элемент.
<rect x="100" y="100" width="100" height="100" />
<text>Some Text</text>
Есть идеи?
7 ответов
var bbox = textElement.getBBox();
var width = bbox.width;
var height = bbox.height;
и затем установите атрибуты прямоугольника соответственно.
Ссылка на сайт: getBBox()
в стандарте SVG v1.1.
document.getElementById('yourTextId').getComputedTextLength();
работал для меня в
Что касается длины текста, ссылка указывает на то, что BBox и getComputedTextLength() могут возвращать слегка отличающиеся значения, но те, которые довольно близки друг к другу.
Как насчет что-то вроде этого для совместимости:
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
вернет хорошие результаты, поэтому нет вращения и, вероятно, нет масштабирования (?)
Не уверен, почему, но ни один из вышеперечисленных методов не работает для меня. У меня был некоторый успех с методом canvas, но мне пришлось применять все виды масштабных факторов. Даже с учетом масштабных факторов у меня все еще были противоречивые результаты между 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-текста.
Спецификация SVG имеет специальный метод для возврата этой информации: getComputedTextLength()
var width = textElement.getComputedTextLength(); // returns a pixel number
Если вы используете NodeJS и хотите, чтобы ваше приложение оставалось легким (например, не используйте безголовый браузер). Решение, которое можно использовать, - это предварительно рассчитать размер шрифта.
Используя: https://github.com/nicktaras/getFontCharWidth, вы можете определить ширину каждого символа в шрифте.
Затем, например, в приложении node js вы можете перебирать каждый символ, чтобы вычислить фактическую ширину.