SVG는 텍스트 요소 너비를 얻습니다.


106

나는 SVG 파일을 일부 ECMAScript를 / 자바 스크립트 작업을하고 얻을 필요가있어 widthheighttext내가 그것을 둘러싸는 사각형의 크기를 조절할 수 있도록 요소입니다. HTML에서는 요소 의 offsetWidthoffsetHeight속성 을 사용할 수 있지만 해당 속성을 사용할 수없는 것으로 보입니다.

작업해야 할 부분이 있습니다. 텍스트를 변경할 때마다 사각형의 너비를 변경해야하는데 요소 의 실제 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 (). width와 동일한 결과를 반환 했으므로 너비와 높이가 모두 필요했기 때문에 경계 상자를 사용했습니다. 텍스트 길이가 너비와 다른 상황이 있는지 확실하지 않습니다. 텍스트를 여러 줄로 분할하는 것과 같은 텍스트 레이아웃을 돕기 위해 해당 방법이 제공되는 반면 경계 상자는 사용 가능한 일반적인 방법이라고 생각합니다. 모든 (?) 요소.
NickFitz

2
문제는 텍스트가 경로를 따르는 경우 너비가 텍스트의 실제 너비가 아니라는 것입니다 (예를 들어 텍스트가 원 경로를 따르는 경우 bbox는 원을 둘러싸는 상자이고 너비를 얻는 것은 그렇지 않습니다. 원을 둘러싼 텍스트 이전의 너비가 아닙니다.) 또 다른 것은 bbox.width가 2 배 더 크다는 것입니다. 따라서 element inspector가 200의 너비를 표시하면 bbox.width는 400입니다. 이유를 모르겠습니다.
trusktr 2016

그려지기 전에 길이를 어떻게 계산할 수 있습니까?
Nikos

7

텍스트 길이와 관련하여 링크는 BBox와 getComputedTextLength ()가 약간 다른 값을 반환 할 수 있지만 서로 상당히 가까운 값을 반환 할 수 있습니다.

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


링크 감사합니다 !! 혼자서 모든 시나리오를 살펴 봐야했지만 이것은 정말 좋은 요약입니다 ... 내 문제는 파이어 폭스의 Tspan 요소에 getBBox ()를 사용하는 것이 었습니다 ... 더 구체적이고 성가신 문제는 아닙니다 .. 다시 한 번 감사합니다!
안드레스 엘리 손도

4
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에서만 작동했습니다. 크롬과 사파리를 구하기 위해 요소를 확장하지만 기쁨은 없습니다. 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;
    }

글쎄, 그게 다야. 무차별 대입이지만 세 가지 브라우저 모두에서 매우 정확하며 내 좌절 수준이 0으로 떨어졌습니다. 브라우저가 진화함에 따라 얼마나 오래 지속 될지 확실하지 않지만 SVG 텍스트에 대한 강력한 글꼴 메트릭 기술을 개발하기에 충분히 길어야합니다.


지금까지 최고의 솔루션! 공유해 주셔서 감사합니다!
just_user

이것은 커닝을 처리하지 않습니다
pat

사실이지만 내가 사용하는 글꼴은 중요하지 않습니다. 시간이되면 내년에 다시 방문 할 예정입니다. 무차별 대입 방식을 사용하지 않는 사례에 대해 텍스트 메트릭을 더 정확하게 만드는 방법에 대한 아이디어가 있습니다.
agent-p

큰. SVG 차트 레이블을 로컬 스크립트에서 즉시 정렬하지 않고 정적으로 정렬 할 수 있습니다. RoR에서 Ruby로 차트를 제공 할 때 삶이 훨씬 더 간단 해집니다. 감사!
Lex Lindsey

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.