LoginSignup
0
1

More than 5 years have passed since last update.

テキストの長さを元に、divなどに収まるサイズのフォントサイズを計算するJSのメモ

Last updated at Posted at 2019-01-09

なんかCSSとかJSでライブラリすでにありそうだなーって思いつつも自前で実装したほうが早そうだったので書いたら、既にCanvasで簡単かつ正確に取得できたので、両方残しておきます。

やりたいこと

以下条件をもつときに、見切れたりすることなくテキストを表示したい。

  • divやpタグ内にテキストを挿入し表示する。
  • divやpタグの横幅は、一定ではない。
  • 表示したいテキストの文字数は一定ではない。
  • テキストは横向きに1行で表示し、改行はしない。ありえない。

補足:

  • ViewportWidth 等は、テキストが一定ではないため使え無い。

実装(Goodな方)

コメント でおしえていただきました。

非表示なcanvasを追加して、 TextMetrics ctx.measureText(text); で計測する方法。

JS & HTML

<canvas id="canvasForMeasureText" style="display: none;"></canvas>
// 以下HTMLを追加しないと動かない。JSで追加する処理かいてない。
// <canvas id="canvasForMeasureText" style="display: none;"></canvas>

function makeBetterFontSize(boxWidthPx, text) {
  // Decorations for measure Text:
  // `fontName` is like `ヒラギノ角ゴ Pro W3`.
  // `fontSize` is Number.
  let decorations = {
    fontName: 'ヒラギノ角ゴ Pro W3',
    fontSize: 15,
  };
  // Decorate a ctx
  function decorateCtx(originalCtx, decorationSettings) {
    let ctx = originalCtx;
    if (decorationSettings.fontSize && decorationSettings.fontName) {
      const fontSetting = decorationSettings.fontSize + 'px "' + decorationSettings.fontName + '"';
      ctx.font = fontSetting
    }
    return ctx
  }

  // Default is 15px for my project.
  let fontSize = 30;
  if (boxWidthPx < 1) {
    console.warn('`boxWidthPx` must be over 1.');
    return fontSize;
  }
  if (typeof boxWidthPx !== 'number') {
    console.warn('`boxWidthPx` is not Number. return default font-size: ' + typeof boxWidthPx);
    return fontSize;
  }
  if (typeof text !== 'string') {
    console.warn('`text` is not String. return default font-size.' + typeof text);
    return fontSize;
  }

  // Setup decoration settings.
  while(fontSize > 1) {
    const canvas = document.getElementById('canvasForMeasureText');
    if (!canvas) {
      console.warn('`canvasForMeasureText` is null. return default font-size.');
      break;
    }
    const ctx = canvas.getContext('2d');
    decorations.fontSize = fontSize;
    const decoratedCtx = decorateCtx(ctx, decorations);
    const textMetrics = decoratedCtx.measureText(text);
    console.log(' ', 'text metrics', textMetrics, ' <=', boxWidthPx);
    if (textMetrics.width <= boxWidthPx) {
      break;
    }
    fontSize -= 1;
  }
  return fontSize;
}

検証用のコード

var boxWidth = 271;
var text = "99.99999999";
console.log("-", makeBetterFontSize(boxWidth, text));
// 30

boxWidth = 271;
text = "999999999.99999999";
console.log("A", makeBetterFontSize(boxWidth, text));
// 23

boxWidth = 271;
text = "9999999999999.99999999";
console.log("B", makeBetterFontSize(boxWidth, text));
// 19

boxWidth = 300;
text = "9999999999999.99999999";
console.log("C", makeBetterFontSize(boxWidth, text));
// 21

boxWidth = 30;
text = "9999999999999.99999999";
console.log("D", makeBetterFontSize(boxWidth, text));
// 2

boxWidth = 20;
text = "9999999999999.99999999";
console.log("E", makeBetterFontSize(boxWidth, text));
// 1

boxWidth = 271;
text = "9.99999999";
console.log("F", makeBetterFontSize(boxWidth, text));
// 30

boxWidth = "あああ";
text = "";
console.log("G", makeBetterFontSize(boxWidth, text));
// 30
// ⚠️がでる。使い方が間違ってる。

boxWidth = 300;
text = [];
console.log("H", makeBetterFontSize(boxWidth, text));
// 30
// ⚠️がでる。使い方が間違ってる。

boxWidth = 0;
text = "111.0";
console.log("I", makeBetterFontSize(boxWidth, text));
// 30
// ⚠️がでる。使い方が間違ってる。

実装(NoGoodな方)

使用するフォントにより数値が不正確になってしまうため、Goodな方をつかったほうがよい。

function

function divideBoxwitdhAndPxPerText(boxWidthPx, pxPerText) {
  return boxWidthPx / pxPerText;
}

function makeBetterFontSize(boxWidthPx, text){
  // Default is 15.(font-size:30px) for my proj.
  var pxPerText = 15;
  if (boxWidthPx < 1) {
    console.warn('`boxWidthPx` must be over 1.');
    return pxPerText * 2;
  }
  if (typeof boxWidth !== 'number') {
    console.warn('`boxWidthPx` is not Number. return default font-size.');
    return pxPerText * 2;
  }
  if (typeof text !== 'string') {
    console.warn('`text` is not String. return default font-size.');
    return pxPerText * 2;
  }
  while(pxPerText > 0) {
    var maxTextLength = divideBoxwitdhAndPxPerText(boxWidthPx, pxPerText);
    if (text.length <= maxTextLength) {
      break;
    }
    pxPerText -= 1;
  }
  if (pxPerText === 0) {
    pxPerText = 0.5;
  }
  // Font-size is `pxPerText * 2` for my proj.
  return pxPerText * 2;
}

検証用のコード

var boxWidth = 271;
var text = "999999999.99999999";
console.log("A", makeBetterFontSize(boxWidth, text));
// 30

boxWidth = 271;
text = "9999999999999.99999999";
console.log("B", makeBetterFontSize(boxWidth, text));
// 26

boxWidth = 300;
text = "9999999999999.99999999";
console.log("C", makeBetterFontSize(boxWidth, text));
// 30

boxWidth = 30;
text = "9999999999999.99999999";
console.log("D", makeBetterFontSize(boxWidth, text));
// 2

boxWidth = 20;
text = "9999999999999.99999999";
console.log("E", makeBetterFontSize(boxWidth, text));
// 1

boxWidth = 271;
text = "9.99999999";
console.log("F", makeBetterFontSize(boxWidth, text));
// 30

boxWidth = "あああ";
text = "";
console.log("G", makeBetterFontSize(boxWidth, text));
// 30
// ⚠️がでる。使い方が間違ってる。

boxWidth = 300;
text = [];
console.log("H", makeBetterFontSize(boxWidth, text));
// 30
// ⚠️がでる。使い方が間違ってる。

boxWidth = 0;
text = "111.0";
console.log("I", makeBetterFontSize(boxWidth, text));
// 30
// ⚠️がでる。使い方が間違ってる。

補足

boxWidth には実際のpxが入るので以下みたいなのをつかう想定。

var boxWidth = document.getElementById('my-id').clientWidth;

最後に

NoGoodな方も、何かの役に立つかも知れないので残しておきました。コメントで書いていましたが、誤差でるとおもってたら結構あるようで、おしえてもらってよかったという気持ち。

以上。

0
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1