SVG получить ширину текстового элемента - PullRequest
96 голосов
/ 28 октября 2009

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

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

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

Есть идеи?

Ответы [ 6 ]

141 голосов
/ 28 октября 2009
var bbox = textElement.getBBox();
var width = bbox.width;
var height = bbox.height;

и затем установите атрибуты прямоугольника соответственно.

Ссылка: getBBox() в стандарте SVG v1.1.

7 голосов
/ 01 июня 2014

Что касается длины текста, ссылка указывает на то, что BBox и getComputedTextLength () могут возвращать немного разные значения, но те, которые довольно близки друг к другу.

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

2 голосов
/ 30 марта 2017
document.getElementById('yourTextId').getComputedTextLength();

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

2 голосов
/ 15 марта 2017

Не знаю почему, но ни один из вышеперечисленных методов не работает для меня У меня был некоторый успех с методом 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);
            var space_string_width = div.clientWidth;
            document.body.removeChild(div);
            var no_space_string = "!!";
            div.innerHTML = no_space_string;
            document.body.appendChild(div);
            var no_space_string_width = div.clientWidth;
            console.log("space width: " + (space_string_width - no_space_string_width));
            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-текста.

2 голосов
/ 11 июня 2015

Как насчёт совместимости:

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 голосов
/ 10 мая 2018

Спецификация SVG имеет специальный метод для возврата этой информации: getComputedTextLength()

var width = textElement.getComputedTextLength(); // returns a pixel number
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...