前置き
フォームに入力された文字列を pixi.js で描画するものを作っているときに遭遇した事象です。
ちなみに僕自身、 pixi.js は reference 見ながらなんとなく実装できるくらいです。
何が起きた?
const style = {
fill: "#fff",
fontSize: 40,
wordWrap: true,
breakWords: true,
wordWrapWidth: 100,
};
const message = new PIXI.Text("🤗🤗🤗🤗🤗", style);
container.addChild(message);
// output:
// 🤗🤗�
// �🤗🤗
サロゲートペアの部分で分断されて出力されてしまいました。
対処
- サロゲートペアの判定をする
- TextMetrics を取得する
- 文字列の長さを比較する (元の文字列・改行を考慮した文字列)
- 長さが合わなかったら wordWrapWidth を調整する
// サロゲートペア判定
function stringToArray(str: string) {
return str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF]/g) || [];
}
const text = "🤗🤗🤗🤗🤗";
const style = {
fill: "#fff",
fontSize: 40,
wordWrap: true,
breakWords: true,
wordWrapWidth: 100,
};
// TextMetrics 取得
const textMetrics = PIXI.TextMetrics.measureText(text, style);
// 文字列の長さ
const beforeTextLength = stringToArray(text).length;
// 行ごとの文字列の長さを足した数
let afterTextLength = textMetrics.lines.reduce((pv, v) => pv + stringToArray(v).length, 0);
// output: 5 6
console.log(beforeTextLength, afterTextLength);
// 文字サイズ分横幅を増やして試す
let retry = style.fontSize;
let wordWrapWidth = style.wordWrapWidth;
// retry上限回数 または 文字列の長さが同じ になるまで試す
while (retry > 0 && beforeTextLength !== afterTextLength) {
retry -= 1;
wordWrapWidth += 1;
afterTextLength = PIXI.TextMetrics
.measureText(text, { ...style, wordWrapWidth })
.lines.reduce((pv, v) => pv + stringToArray(v).length, 0);
}
const message = new PIXI.Text(text, { ...style, wordWrapWidth });
container.addChild(message);
// output:
// 🤗🤗🤗
// 🤗🤗
最初にこの事象に遭遇したときは、もっと複雑なことしないといけないのかなーと思ったんですが、意外となんとかなりました。