なんか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な方も、何かの役に立つかも知れないので残しておきました。コメントで書いていましたが、誤差でるとおもってたら結構あるようで、おしえてもらってよかったという気持ち。
以上。