[AutoReSizedText] AutoShrinkText

Darcy Daeseok YU ·2026년 3월 23일
'use client';

import { useLayoutEffect, useRef, useState } from 'react';

interface Props {
  text: string;
  baseFontSize?: number; // 시작 폰트 크기 (기본 12px = --font-size-xs)
  className?: string; // 추가 스타일 (기본 font-mono)
  textAlign?: 'left' | 'right' | 'center';
  maxWidth?: number;
}

export default function AutoShrinkText({
  text,
  baseFontSize = 12,
  className = 'font-mono',
  textAlign = 'left',
  maxWidth: _maxWidth,
}: Props) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [fontSize, setFontSize] = useState(baseFontSize);

  useLayoutEffect(() => {
    if (!containerRef.current) return;

    const maxWidth = _maxWidth ?? containerRef.current.offsetWidth;
    if (maxWidth <= 0) return;

    // 1. 브라우저에서 실제로 적용된 폰트 패밀리를 가져옴 (GeistMonoVF 인식)
    const computedStyle = window.getComputedStyle(containerRef.current);
    const fontFamily = computedStyle.fontFamily;

    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    if (!context) return;

    // 2. 폰트 크기 정밀 계산 루프
    let currentSize = baseFontSize;
    context.font = `${currentSize}px ${fontFamily}`;
    let textWidth = context.measureText(text).width;

    // 컨테이너 너비를 넘는 경우 0.1px 단위로 축소 (최소 5px까지)
    if (textWidth > maxWidth) {
      while (textWidth > maxWidth && currentSize > 5) {
        currentSize -= 0.1;
        context.font = `${currentSize}px ${fontFamily}`;
        textWidth = context.measureText(text).width;
      }
      setFontSize(currentSize);
    } else {
      setFontSize(baseFontSize); // 텍스트가 짧으면 기본값 유지
    }
  }, [text, baseFontSize, _maxWidth]);

  const justifyContent =
    textAlign === 'right'
      ? 'flex-end'
      : textAlign === 'left'
        ? 'flex-start'
        : textAlign;
  return (
    <p
      ref={containerRef}
      className={className}
      style={{
        width: '100%',
        maxWidth: _maxWidth,
        overflow: 'hidden',
        whiteSpace: 'nowrap',
        display: 'flex',
        alignItems: 'center',
        lineHeight: 1,
        justifyContent: justifyContent,
      }}
    >
      <span
        style={{
          fontSize: `${fontSize}px`,
          display: 'inline-block',
        }}
      >
        {text}
      </span>
    </p>
  );
}

profile
React, React-Native https://darcyu83.netlify.app/

0개의 댓글