SVG lấy chiều rộng phần tử văn bản


105

Tôi đang làm việc trên một số ECMAScript / JavaScript cho tệp SVG và cần lấy widthheightcủa một textphần tử để tôi có thể thay đổi kích thước một hình chữ nhật bao quanh nó. Trong HTML, tôi có thể sử dụng các thuộc tính offsetWidthoffsetHeighttrên phần tử nhưng có vẻ như các thuộc tính đó không khả dụng.

Đây là một phần mà tôi cần làm việc với. Tôi cần thay đổi chiều rộng của hình chữ nhật bất cứ khi nào tôi thay đổi văn bản nhưng tôi không biết cách lấy giá trị thực width(tính bằng pixel) của textphần tử.

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

Bất kỳ ý tưởng?

Câu trả lời:


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

và sau đó đặt các thuộc tính của trực tràng cho phù hợp.

Liên kết: getBBox()trong tiêu chuẩn SVG v1.1.


4
Cảm ơn. Tôi cũng tìm thấy một hàm khác có thể giúp ích cho chiều rộng. textElement.getComputedTextLength (). Tôi sẽ thử cả hai tối nay và xem cái nào phù hợp hơn với tôi.
Stephen Sorensen

5
Tôi đã chơi với những thứ này tuần trước; phương thức getComputedTextLength () trả về cùng một kết quả là getBBox (). width cho những thứ tôi đã thử, vì vậy tôi chỉ sử dụng hộp giới hạn, vì tôi cần cả chiều rộng và chiều cao. Tôi không chắc liệu có bất kỳ trường hợp nào khi chiều dài văn bản khác với chiều rộng hay không: Tôi nghĩ có lẽ phương pháp đó được cung cấp để trợ giúp bố cục văn bản chẳng hạn như tách văn bản thành nhiều dòng, trong khi hộp giới hạn là một phương pháp chung khả dụng trên tất cả các phần tử (?).
NickFitz

2
Vấn đề là, nếu văn bản đi theo một đường dẫn, thì chiều rộng không phải là chiều rộng thực của văn bản (ví dụ: nếu văn bản đi theo một đường tròn thì hộp bbox là đường bao quanh vòng tròn và nhận được chiều rộng của nó 't chiều rộng của văn bản trước khi nó đi xung quanh vòng tròn). Một điều khác là bbox.width lớn hơn hệ số 2, vì vậy nếu trình kiểm tra phần tử hiển thị chiều rộng là 200, thì bbox.width là 400. Tôi không chắc tại sao.
trusktr

Làm thế nào bạn có thể tính ra độ dài trước khi nó được vẽ?
Nikos

7

Về độ dài của văn bản, liên kết dường như cho biết BBox và getComputedTextLength () có thể trả về các giá trị hơi khác nhau, nhưng các giá trị này khá gần nhau.

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


Cảm ơn vì mối lên kết đó!! Tôi đã phải tự mình vượt qua tất cả các tình huống, nhưng đây là một bản tóm tắt thực sự tốt ... Vấn đề của tôi là sử dụng getBBox () trên một phần tử Tspan trên firefox ... Đó không thể là một vấn đề cụ thể và khó chịu hơn .. . Cảm ơn một lần nữa!
Andres Elizondo

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

đã làm việc cho tôi trong


Giải pháp của bạn trả về độ dài thực tế của phần tử văn bản là câu trả lời đúng. Một số câu trả lời sử dụng getBBox () có thể đúng nếu văn bản không được xoay.
Netsi1964

2

Làm thế nào về một cái gì đó như thế này để tương thích:

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

Điều này trả về giá trị trung bình của bất kỳ kết quả hợp lệ nào của ba phương pháp. Bạn có thể cải thiện nó để loại bỏ các ngoại lệ hoặc ưu tiên getComputedTextLengthnếu phần tử là phần tử văn bản.

Cảnh báo: Như nhận xét đã nói, getBoundingClientRectrất khó. Xóa nó khỏi các phương thức hoặc chỉ sử dụng điều này trên các phần tử getBoundingClientRectsẽ trả lại kết quả tốt, do đó, không xoay vòng và có thể không chia tỷ lệ (?)


1
getBoundsClientRect hoạt động trong một hệ thống điều phối có khả năng khác với hai hệ thống kia.
Robert Longson

2

Không chắc tại sao, nhưng không có phương pháp nào ở trên hiệu quả với tôi. Tôi đã có một số thành công với phương pháp canvas, nhưng tôi phải áp dụng tất cả các loại yếu tố tỷ lệ. Ngay cả với các yếu tố tỷ lệ, tôi vẫn có kết quả không nhất quán giữa Safari, Chrome và Firefox.

Vì vậy, tôi đã thử những cách sau:

            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;

Hoạt động tuyệt vời và siêu chính xác, nhưng chỉ trong Firefox. Quy mô các yếu tố để giải cứu cho Chrome và Safari, nhưng không có niềm vui. Hóa ra lỗi Safari và Chrome không tuyến tính với độ dài chuỗi hoặc kích thước phông chữ.

Vì vậy, hãy tiếp cận số hai. Tôi không quan tâm nhiều đến cách tiếp cận vũ phu, nhưng sau khi vật lộn với điều này và tắt trong nhiều năm, tôi quyết định thử nó. Tôi quyết định tạo các giá trị không đổi cho từng ký tự có thể in riêng lẻ. Thông thường điều này sẽ khá tẻ nhạt, nhưng may mắn là Firefox hoạt động siêu chính xác. Đây là giải pháp vũ phu hai phần của tôi:

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

Lưu ý: Đoạn mã trên phải được thực thi trong Firefox để tạo ra một mảng giá trị chính xác. Ngoài ra, bạn phải thay thế mục mảng 32 bằng giá trị chiều rộng khoảng trắng trong nhật ký bảng điều khiển.

Tôi chỉ cần sao chép văn bản Firefox trên màn hình và dán nó vào mã javascript của mình. Bây giờ tôi có mảng độ dài ký tự có thể in được, tôi có thể triển khai hàm get width. Đây là mã:

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

Đó là nó. Brute force, nhưng nó siêu chính xác trong cả ba trình duyệt và mức độ thất vọng của tôi đã xuống 0. Không chắc nó sẽ tồn tại trong bao lâu khi các trình duyệt phát triển, nhưng sẽ đủ lâu để tôi phát triển một kỹ thuật đo lường phông chữ mạnh mẽ cho văn bản SVG của mình.


Giải pháp tốt nhất cho đến nay! Cám ơn vì đã chia sẻ!
just_user

Điều này không xử lý kerning
vỗ vào

Đúng, nhưng không quan trọng đối với phông chữ tôi sử dụng. Tôi sẽ thăm lại điều này vào năm sau khi tôi có thời gian. Tôi có một số ý tưởng về việc làm cho số liệu văn bản chính xác hơn cho các trường hợp của tôi không sử dụng phương pháp vũ phu.
agent-p

Tuyệt quá. Cho phép tôi căn chỉnh tĩnh các nhãn biểu đồ SVG thay vì phải căn chỉnh chúng nhanh chóng trong tập lệnh cục bộ. Điều đó làm cho cuộc sống đơn giản hơn rất nhiều khi phục vụ các biểu đồ trong Ruby từ RoR. Cảm ơn!
Lex Lindsey

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.