SVG รับความกว้างขององค์ประกอบข้อความ


107

ฉันกำลังทำงานกับ ECMAScript / JavaScript สำหรับไฟล์ SVG และจำเป็นต้องได้รับwidthและheightของtextองค์ประกอบเพื่อที่ฉันจะสามารถปรับขนาดสี่เหลี่ยมผืนผ้าที่ล้อมรอบได้ ใน HTML ฉันจะสามารถใช้แอตทริบิวต์offsetWidthand offsetHeightกับองค์ประกอบได้ แต่ดูเหมือนว่าคุณสมบัติเหล่านั้นไม่สามารถใช้งานได้

นี่คือส่วนที่ฉันต้องทำงานด้วย ฉันต้องการเปลี่ยนความกว้างของสี่เหลี่ยมทุกครั้งที่ฉันเปลี่ยนข้อความ แต่ฉันไม่รู้วิธีรับจริงwidth(เป็นพิกเซล) ของtextองค์ประกอบ

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

ความคิดใด ๆ ?

คำตอบ:


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

จากนั้นตั้งค่าคุณลักษณะของ rect ตามนั้น

ลิงก์: getBBox()ในมาตรฐาน SVG v1.1


4
ขอบคุณ. ฉันยังพบฟังก์ชันอื่นที่สามารถช่วยเรื่องความกว้างได้ textElement.getComputedTextLength () ฉันจะลองทั้งสองคืนนี้และดูว่าอันไหนดีกว่าสำหรับฉัน
Stephen Sorensen

5
ฉันเล่นด้วยเมื่อสัปดาห์ที่แล้ว เมธอด getComputedTextLength () ส่งคืนผลลัพธ์เดียวกันกับ getBBox () ความกว้างสำหรับสิ่งที่ฉันทดลองใช้ดังนั้นฉันจึงใช้กล่องขอบเขตเพราะฉันต้องการทั้งความกว้างและความสูง ฉันไม่แน่ใจว่ามีสถานการณ์ใดบ้างที่ความยาวของข้อความจะแตกต่างจากความกว้าง: ฉันคิดว่าวิธีนี้อาจมีไว้เพื่อช่วยในการจัดวางข้อความเช่นการแบ่งข้อความในหลายบรรทัดในขณะที่กล่องขอบเขตเป็นวิธีการทั่วไปที่มีให้ ในทุกองค์ประกอบ (?)
NickFitz

2
ปัญหาคือถ้าข้อความเป็นไปตามเส้นทางแสดงว่าความกว้างไม่ใช่ความกว้างจริงของข้อความ (ตัวอย่างเช่นถ้าข้อความเป็นไปตามเส้นทางวงกลม bbox จะเป็นข้อความที่ล้อมรอบวงกลมและได้รับความกว้างของนั้น ความกว้างของข้อความก่อนที่จะวนรอบวงกลม) อีกอย่างก็คือ bbox.width มากกว่าด้วยปัจจัย 2 ดังนั้นหากตัวตรวจสอบองค์ประกอบแสดงความกว้าง 200 แสดงว่า bbox.width คือ 400 ฉันไม่แน่ใจว่าทำไม
trusktr

คุณจะคำนวณความยาวก่อนวาดได้อย่างไร?
Nikos

7

เกี่ยวกับความยาวของข้อความลิงก์ดูเหมือนว่าจะระบุ BBox และ getComputedTextLength () อาจส่งคืนค่าที่แตกต่างกันเล็กน้อย แต่ลิงก์ที่อยู่ใกล้กันพอสมควร

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


ขอบคุณสำหรับลิงค์ !! ฉันต้องผ่านสถานการณ์ทั้งหมดด้วยตัวเอง แต่นี่เป็นบทสรุปที่ดีจริงๆ ... ปัญหาของฉันคือการใช้ getBBox () ในองค์ประกอบ Tspan บน firefox ... มันไม่สามารถเป็นปัญหาที่เฉพาะเจาะจงและน่ารำคาญมากขึ้น .. . ขอบคุณอีกครั้ง!
Andres Elizondo

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

ทำงานให้ฉันใน


วิธีแก้ปัญหาของคุณจะส่งกลับความยาวจริงขององค์ประกอบข้อความซึ่งเป็นคำตอบที่ถูกต้อง คำตอบบางคำใช้ getBBox () ซึ่งอาจถูกต้องหากไม่หมุนข้อความ
Netsi1964

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จะให้ผลลัพธ์ที่ดีดังนั้นจึงไม่มีการหมุนและอาจไม่มีการปรับขนาด (?)


1
getBoundingClientRect ทำงานในระบบการประสานงานที่อาจแตกต่างกันกับอีกสองระบบ
Robert Longson

2

ไม่แน่ใจว่าทำไม แต่วิธีการข้างต้นไม่ได้ผลสำหรับฉัน ฉันประสบความสำเร็จกับวิธีการแคนวาส แต่ต้องใช้สเกลแฟกเตอร์ทุกประเภท แม้จะมีปัจจัยด้านขนาดฉันก็ยังได้ผลลัพธ์ที่ไม่สอดคล้องกันระหว่าง 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 บนหน้าจอและวางลงในโค้ดจาวาสคริปต์ของฉัน ตอนนี้ฉันมีอาร์เรย์ของความยาวอักขระที่พิมพ์ได้แล้วฉันสามารถใช้ฟังก์ชัน 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 ของฉัน


ทางออกที่ดีที่สุด! ขอบคุณสำหรับการแบ่งปัน!
just_user

สิ่งนี้ไม่ได้จัดการกับ
pat

จริง แต่ไม่สำคัญสำหรับฟอนต์ที่ฉันใช้ ฉันจะกลับมาอีกครั้งในปีหน้าเมื่อฉันมีเวลา ฉันมีความคิดบางอย่างเกี่ยวกับการทำให้เมตริกข้อความแม่นยำยิ่งขึ้นสำหรับกรณีของฉันที่ไม่ใช้วิธีการบังคับแบบดุร้าย
ตัวแทน -p

เยี่ยมมาก ช่วยให้ฉันสามารถจัดแนวป้ายแผนภูมิ SVG แบบคงที่แทนที่จะต้องจัดแนวทันทีในสคริปต์ท้องถิ่น ทำให้ชีวิตง่ายขึ้นมากเมื่อแสดงชาร์ตใน Ruby จาก RoR ขอบคุณ!
Lex Lindsey

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.