작동 화면에서 div 리사이징 기능은 react-grid-layout 라이브러리를 이용해서 구현한 것.

작동 화면에서 div 리사이징 기능은 react-grid-layout 라이브러리를 이용해서 구현한 것.

개요

사내 프로젝트에서 react-grid-layout 을 이용해 대시보드 페이지의 위젯을 만들면서, 컨테이너 크기에 비례하는 크기를 가지는 텍스트 컴포넌트를 만들어야 했다.

처음엔 미디어쿼리를 이용해서 만들려고 했으나, 브레이크 포인트를 지정하기가 애매한 상황이였고,

react-fittext 라는 라이브러리를 사용하려고 했으나, 요구사항을 모두 충족시키기에 부족한 점이 있어서 직접 구현하기로 했다.

Dependencies

react-resize-detector 라이브러리가 필요하다.

react-resize-detector는 window.addEventListener(”resize”, handleResize); 처럼, 지정한 React Element의 사이즈가 변경될 때 마다 어떤 작동을 수행하거나, width, height 값을 구할 수 있도록 도와주는 라이브러리 이다.

작동 원리

텍스트 영역(<text>)의 bounding box의 정보(width, height)와 컨테이너 영역(<div>)의 width, height의 비율을 계산해서, 텍스트 영역(<text>)을 그 비율만큼 scale 시켜주는 것이다.

svg로 구현하기 때문에 scale 되더라도 이미지가 깨지는 일이 없다.

소스 코드

import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useResizeDetector } from 'react-resize-detector';

const ResponsiveText = ({ children }) => {
  const textRef = useRef(null);

  const [ContainerW, setContainerW] = useState(1);
  const [ContainerH, setContainerH] = useState(1);

  useEffect(() => {
    const textElement = textRef.current;

    if (textEl) {
			// getBBox를 이용해서 <text> 가 가지고있는 bounding box의 정보를 구한다.
			// * getBBox는 scale 같은 속성에 영향을 받지 않은 상태의 bounding box를 리턴한다.
      const textBox = textElement.getBBox();
      const textW = textBox.width;
      const textH = textBox.height;

			// scale 시킬 비율을 구한다.
      const wRatio = ContainerW / textW;
      const hRatio = ContainerH / textH;

			// 텍스트가 넘치지 않도록 하기 위해 
      const ratio = wRatio < hRatio ? wRatio : hRatio;

      // 좌측 정렬
      // const hAlign = 0;

      // 가운데 정렬
      const hAlign = (ContainerW - textW * ratio) / 2;

      // 우측 정렬
      // const hAlign = ContainerW - textW * ratio;

			// matrix function의 인자 의미는 다음과 같다.
			// matrix(scaleX, skewY, skewX, scaleY, translateX, translateY)
      textEl.setAttribute(
        'transform',
        `matrix(${ratio}, 0, 0, ${ratio}, ${hAlign}, 0)`,
      );
    }
  }, [ContainerW, ContainerH]);

	// useResizeDetector의 핸들러
	// w, h는 ref를 지정한 React Element의 width, height이다.
  const onResize = useCallback((w, h) => {
    setContainerW(w);
    setContainerH(h);
  }, []);

	// useResizeDetector가 React Element의 사이즈 변경을 감지한다.
  const { ref: containerRef } = useResizeDetector({ onResize });

  return (
    <div ref={containerRef}>
      <text x="0" y="0" ref={textRef} fill="#000000">
        {children}
      </text>
    </div>
  );
};

문제점 및 개선