[Javascript] SVG 태그의 <text>에서 말줄임표 구현하기

TL;DR;

  • svg의 text 태그에 말줄임표 스타일 속성(ellipsis) 적용이 안된다.
  • 텍스트 전체 길이와 svg의 text 요소 너비를 계산하여 말줄임표 문자를 직접 추가해주면 말줄임표 효과를 적용할 수 있다.

서론

css 속성 중 text-overflowellipsis 을 이용하면, 텍스트가 너비를 벗어날 때 간단히 말줄임표 처리를 할 수 있다.

undefined

참고) https://developer.mozilla.org/en-US/docs/Web/CSS/text-overflow

위 마크업에서 svg의 text 요소를 사용한 것으로 수정하면 아래와 같다.

  • 하지만 svg의 text 요소에는 말줄임표가 적용되어있지 않다.
undefined
  • svg 태그에 적용할 수 있는 css 속성은 span이나 div 처럼 일반적인 DOM 요소에 적용할 수 있는 속성과 차이가 있기 때문에 스타일 속성을 그대로 적용할 수 없다!
✨ ** svg 요소 별 css 속성 정리표** https://css-tricks.com/svg-properties-and-css/

그래서 구글에 검색을 해보면 text 요소에 말줄임표 효과를 적용하기 위한 여러 글이 있는 것을 확인할 수 있다.

이번 글은 이러한 방법들 중 하나를 소개하는 글이다. (코드 예시는 Typescript로 작성함)

설명

1. 함수 정의 및 매개변수

async function trimTextToFitWidth(
  textElement: SVGTextElement,
  maxWidth: number,
  originText: string = ''
)

  • 여러 text 요소에 대해 효율적으로 말줄임표 처리를 하기 위해 비동기 함수로 정의
  • textElement 가 바로 text 요소를 나타내는 매개변수를 의미
  • maxWidth는 텍스트가 들어갈 수 있는 최대 너비

2. 변수 초기화

let text = originText;
const ellipsis = '···';

3. 텍스트 너비 체크 함수 정의

const isTooWide = (str: string) = {
  textElement.textContent = str;
  return textElement.getComputedTextLength()  maxWidth;
};
  • getComputedTextLength() 는 현재 텍스트의 너비를 반환
  • 인자로 문자열을 받고 textElement의 컨텐츠로 할당 후에 최대 너비를 초과하는지 체크

4.🐱‍🏍(핵심) 이분 탐색을 통한 최적의 텍스트 길이 찾기

let start = 0;
let end = text.length;
while (start  end) {
  let mid = Math.floor((start + end) / 2);
  if (isTooWide(text.slice(0, mid) + ellipsis)) {
    end = mid;
  } else {
    start = mid + 1;
  }
}
  • 가장 핵심인 부분
  • 알고리즘 문제 푸는 것도 아니고 무슨 이분 탐색인가 싶지만 textElement 내에서 최대한 자연스럽게 말줄임 스타일을 적용하기 위한 방법
  • 여기서 수행하는 작업의 핵심은 3가지 (1) 텍스트의 가운데 길이에서 말줄임표 텍스트를 더한 문자열이 최대 너비를 초과하는지 체크 (2-1) 초과한다면 현재 텍스트를 절반으로 자르고, 뒤에 있는 문자열을 모두 버린다.(실제 원문 텍스트를 수정하는 것이 아님에 유의!) (2-2) 초과하지 않는다면 최대한 textElement에 긴 텍스트를 쓰기 위해 시작점을 뒤로 하여 텍스트 요소 내의 마지막 끝 문자로 최대한 텍스트 뒤에 있는 문자를 선택

그림으로 보면 매우 간단하다.

undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefined

5. 최종 텍스트 설정

textElement.textContent = text.slice(0, start - 1) + ellipsis;

전체 코드

// text에 ellipsis 효과를 주기 위한 함수
async function trimTextToFitWidth(
  textElement: SVGTextElement,
  maxWidth: number,
  originText: string = ''
) {
  let text = originText;
  const ellipsis = '···';

  const isTooWide = (str: string) = {
    textElement.textContent = str;
    return textElement.getComputedTextLength()  maxWidth;
  };

  // 텍스트가 maxWidth를 넘는지 check
  if (!isTooWide(text)) return;

  // maxWidth 이내에 가장 끝 글자를 바이너리 서치로 탐색
  let start = 0;
  let end = text.length;
  while (start  end) {
    let mid = Math.floor((start + end) / 2);
    if (isTooWide(text.slice(0, mid) + ellipsis)) {
      end = mid;
    } else {
      start = mid + 1;
    }
  }

  // 가장 끝 글자를 지우고 말줄임표 추가
  textElement.textContent = text.slice(0, start - 1) + ellipsis;
}

적용

이제 앞에서 말줄임표가 적용되지 않았던 코드에 적용해본다.

html 문서로 나타내기 위해 Typescript는 Javascript로 변환하여 적용

결과

undefined

물론 ellipsis를 다른 문자로 변경하는 것도 가능 trimTextToFitWidth 함수 내에서 ellipsis 변수 초기화하는 부분을 '...'로 변경하면 아래와 같다.

const ellipsis = "...";

결과

undefined

Profile picture

Written by Kim Soon Yo

IT 생태계의 플랑크톤

Github Link