内部要素のサイズに合わせてfont-sizeを調整するcomponentをhooksで作る

公開:2020/09/22
更新:2020/09/22
2 min読了の目安(約1700字TECH技術記事

記事作成日: 2019/10/13

作ったものメモ。

ふとiOSでadjustsFontSizeToFitWidthという横幅に合わせてフォントサイズが変わるのがあったな、というのを思い出したので再現してみた。

import React, { useState, useRef, useLayoutEffect, useEffect } from "react"
import stringWidth from "string-width"
import styled from "styled-components"

const Container = styled.div`
  width: 100%;
`

const AutoSizedButton = ({ text }) => {
  const ref = useRef()
  const [width, setWidth] = useState(0)
  const [fontSize, setFontSize] = useState("auto")
  useEffect(() => {
    const sizePx = (width / stringWidth(text)) * 2
    setFontSize(`${sizePx}px`)
  }, [width, text])

  useLayoutEffect(() => {
    // @ts-ignore
    const obs = new ResizeObserver((e) => setWidth(e[0].contentRect.width))
    obs.observe(ref.current)
    return () => obs.disconnect()

  }, [])
  return (
    <Container ref={ref} fontSize={fontSize}>
      {text}
    </Container>
  )
}

ResizeObserverを利用している。
まだこれも不安定なツールなので、そこまで実用的なものではない。

フォントサイズの計算として、string-widthを利用している。昔はencodeURIComponentでの計算方法などがあったが、今は3byteで判定されるので使えなくなっている。

カスタムhooksとして切り出すならこんな感じだろう
(が、refsを渡すのはいまいちなので切り出しに向いてない)

const useAutoFontSize = (targetRef, text) => {
  const [width, setWidth] = useState(0)
  const [fontSize, setFontSize] = useState("auto")
  useEffect(() => {
    const sizePx = (width / stringWidth(text)) * 2
    setFontSize(`${sizePx}px`)
  }, [width, text])
  
  useLayoutEffect(() => {
    // @ts-ignore
    const obs = new ResizeObserver((e) => setWidth(e[0].contentRect.width))
    obs.observe(targetRef.current)
    return () => obs.disconnect()
  }, [])
  return fontSize
}