【JS】文字幅を取得する
こんにちは〜
今日はjavascriptでごく稀に使う、文字列の幅を取得する方法についてメモです。
|あああああ|←この||の距離の事です。
javascriptにはデフォルトで計測するものを持っていないので、自力で測るしかありません。
調べてみると、2種類の方法があったので、そちらを使ってみます。
方法2つ
参考サイト:[Javascript] 任意の文字列で、レンダリングに必要な横幅を計算する
1.実際にタグを置いて計算
2.canvasに描いて計算
まぁどちらも参考サイト先に書いてるので、コードは省略します。
じゃあこの記事要らんやん。まぁそう焦らずに。
今回はこの二つのどちらがいいのか、検証したいと思います。
検証1.速度
やはり使うにあたって速度はある程度重要になります。特に何回も呼び出す場合は。
今回はこのようなコードで速度を比較してみました。
//js
var start = performance.now();
for(var i = 0; i < 10000; i++){
var span = document.createElement('span');
span.style.position = 'absolute';
span.style.top = '-1000px';
span.style.left = '-1000px';
span.style.whiteSpace = 'nowrap';
span.innerHTML = 'あいうえお';
span.style.fontSize = '16px';
span.style.fontFamily = '\'Noto Sans\', sans-serif';
document.body.appendChild(span);
var width = span.clientWidth; // (1)
span.parentElement.removeChild(span);
}
console.log( 'span / 実行時間 = ' + (performance.now() - start) + 'ミリ秒' );
var start = performance.now();
for(var i = 0; i < 10000; i++){
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.font = '16px \'Noto Sans\', sans-serif';
var metrics = context.measureText('あいうえお');
var width = metrics.width; // (2)
}
console.log( 'canvas / 実行時間 = ' + (performance.now() - start) + 'ミリ秒' );
詳しいコード内容は参考サイトに、styleはそれぞれのサイトに合わせて記述してください。
では、実行。(※環境はelectronです)
結果:
span / 実行時間 = 1409.0950000099838ミリ秒
canvas / 実行時間 = 190.0600001681596ミリ秒
圧倒的な差!
これはもう言うこと無しですね。
一応、キャッシュを使ってみますか。
//js
var start = performance.now();
var span = document.createElement('span');
span.style.position = 'absolute';
span.style.top = '-1000px';
span.style.left = '-1000px';
span.style.whiteSpace = 'nowrap';
span.style.fontSize = '16px';
span.style.fontFamily = '\'Noto Sans\', sans-serif';
for(var i = 0; i < 10000; i++){
span.innerHTML = 'あいうえお';
document.body.appendChild(span);
var width = span.clientWidth; // (1)
span.parentElement.removeChild(span);
}
console.log( 'span / 実行時間 = ' + (performance.now() - start) + 'ミリ秒' );
var start = performance.now();
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.font = '16px \'Noto Sans\', sans-serif';
for(var i = 0; i < 10000; i++){
var metrics = context.measureText('あいうえお');
var width = metrics.width; // (2)
}
console.log( 'canvas / 実行時間 = ' + (performance.now() - start) + 'ミリ秒' );
表示以外は外に出して、再利用してみます。(もしキャッシュ方法がミスってたらすいません。)
結果:
span / 実行時間 = 1189.5700001623482ミリ秒
canvas / 実行時間 = 26.450000004842877ミリ秒
逆に差が広くなってしまいました(笑)
速度の面ではcanvasが圧倒的に良いですね。
検証2.性能
続いて性能面です。いくら早くても正確な値ではなければ意味がありませんので。
と言うことで、先ほどのコードの(1) (2)の後の行に、それぞれ
console.log('width:', width);
を追記します。(あと、回転数は1万も要らないので、それも減らして・・・)
では文字列「あいうえお」で実行。
span / width: 80
canvas / width: 80
一致しました!
では次。「abcde」で実行してみましょう。
span / width: 49
canvas / width: 48.52796936035156
おや、canvasが少しずれました。
(ちなみにspanは100%正確と仮定します。html上に直接書いてますので。)
では、「あいうえお 」(最後に半角スペース)で実行です。
span / width: 80
canvas / width: 85.32798767089844
spanもしかして前後の半角スペース取り除くの・・・?
では真ん中に入れてみます。「あ い う え お」
span / width: 101
canvas / width: 101.31195068359375
やはり多少ズレますね。
ちなみに書きませんが、全角スペースはズレません。半角・特殊文字のみずれるようです。
ちなみに
canvasがずれた、と書きましたが、実際はspanもずれています。
Element.clientWidth(MDNのサイト)に記載されていますが、
メモ: このプロパティは値を整数値に丸めます。小数値が必要であれば、 element.getBoundingClientRect() を使用してください。
とのことで、綺麗な数値になっているのはただの偶然ではなく、丸め処理をしているからです。
最後の「あ い う え お」を上記関数で実行すると、「101.3125」になります。(でもcanvasとは一致しないですよね。どっちが正しいのか)
結論
特にこだわりがない限りはcanvasでいいでしょう。
一つだけ注意点とすれば、canvasタグはIE8以前では使えないので、もし幅広く対応するサイトの場合はspanのみの選択肢になるかと思います。
それでは、またね〜
- 2020/06/05 追記 -
canvasタグで弱点がありました。
などの特殊文字(文字実体参照っていうのかな?)や、htmlタグには対応してません。
なので、"<span> </span>"を実行すると、spanは5.328125を返すのに対し、canvasは172.17584228515625を返します。
そのため、こういう特殊文字を含む可能性がある場合は、特殊文字を元に戻したり、タグを消したり、諦めてspanを使ったりする必要がありそうです。
逆にspanの場合は、<script>タグなどを埋め込まれると実行してしまう可能性があるので、注意が必要かもしれません。(ちなみに<script>alert("a")</script>を入れてもalertは呼び出されなかったが、念のため。もちろんサイズは0になりました。)