親要素の幅に収まるようにフォントサイズを変更する

Typography is the art and technique of arranging type。

Webフォント(@font-face)や均等割付(text-align: justify;)、日本語と英語の間のスペース調節(text-autospace)などCSS3では文字や文章に関わる表現力も大きく向上している。とはいうもののないものはないので、親要素の幅にちょうど収まるようにフォントを拡大、つまりimg要素にwidth: 100%;を指定した時のようにはCSSではできない(少なくとも思いつかなかった)。というわけでふんだんにjQueryを利用したJavaScriptでどうにかしてみた。

Demo: Resize Font Based on Content Width #6

フォントレンダリングの関係上、Firefoxの方が綺麗なのでスクリーンショットはFirefoxで撮影した。

$(window).load(function () {
  var start = $.now();

  $(".line").each(function () {
    var target = $(this);
    var fontSize = parseInt(target.css("font-size"), 10);

    var temp = $("<div/>").css({
      "display":     "none",
      "font-family": target.css("font-family"),
      "font-size":   fontSize
    }).text(target.text()).appendTo("body");

    var targetWidth = target.width();
    var tempWidth = temp.width();

    while (tempWidth < targetWidth) {
      fontSize *= (targetWidth / tempWidth);
      temp.css("font-size", fontSize + "px");
      tempWidth = temp.width();
    }

    while (tempWidth >= targetWidth) {
      fontSize--;
      temp.css("font-size", fontSize + "px");
      tempWidth = temp.width();
    }

    target.css("font-size", fontSize + "px");
    temp.remove();
  });

  var time = $.now() - start;
  $("body").append($("<p/>").text("Elapsed Time: " + time));
});

まず非表示のdiv要素を作成し、そこにテキストとスタイルをコピーすることによって文を複製する。display: none;だとその要素の幅はfont-sizeに応じて変化するので、幅の割合から大まかに当たりをつけてチェックしていき、元の要素の幅を超えたところでフォントサイズを一旦決定している。その後微調整するために1pxずつ下げていき、ちょうど収まるところで決定という仕組み。2passでやらないとうまく収まらなかったり色々不具合が多かった。

幅の割合から当たりを付けるというアイディアは@ofkに貰った。それに幅のキャッシュと微調整を付けて、大体20ms以下で終了するようになった。OperaとInternet Explorerが安定して高速で、次いでFirefox、ChromeとSafariが若干遅い。

window.onloadで発火している理由は、Webフォントを利用している場合への対策。window.onload前の場合はWebフォントを適用した状態で要素の幅を計算してくれない(ブラウザの実装依存?)ので、ロードが完了してから発火させるようにした。

リサイズに追随しないのでimg要素にwidth: 100%;を指定した時と同一とはいかないが、そこそこ満足のいくものになった。


display: inline-block;な要素をtransform: scale(100%);とかで親と同じ大きさに綺麗に拡大してくれないかなーと妄想して始めたら、全然そんなことはできなかったのでJavaScriptで書いた。