CANVASでの複数行テキスト

掲題のものを、jgame.jsでサポートしてみた。
http://jgame-js.sourceforge.jp/examples-2.0/adventure.html


試しにというか、不思議のすごろくでアイテム手に入れた時とか絶対必要なので、まあ作るしかねーかなって事で作ったんだけど、それなりに大変だったのでメモがてら。

2d contextの現状

canvasに描画するためのオブジェクトである2d contextの現状としては、まあHTML5関連追いかけてる人なら知っていると思うけど、以下2点の問題がある。

  1. 複数行のテキストはサポートされてない
  2. フォントが汚い


2d contextでの文字描画っていうとfillTextとstrokeTextってのが使えるんだけど、まあどっちも複数行はサポートされてない。
それからmeasureTextってのでその文字列の長さがとれるんだけど、このメソッドの実装も結構中途半端で、独自実装もそれなりに手間がかかる。

フォントが汚い件

フォントが汚い件は結局俺も解決できてない。
さっきのリンクは俺の手元のChromeで見たとき、俺が一番綺麗だと思う状態にしたんだけど、FireFoxOperaだと結構レンダリング結果が違う。多分MaciPhoneではまたかなり違ってくると思う。


多分だけど2d contextでアンチエイリアスをかけること自体、WebKitとかの開発チームがてこずるような問題はないと思うんだけど、strokeTextとfillTextって二つサポートされているため、fillTextでアンチエイリアスをかけるってのが迂闊に出来ないんじゃないのと。知らんけど。
まあとにかく汚い。


多分、汚さを許可して下手なアンチエイリアスをかけないのが一番綺麗になるんじゃないのと思う。
これが独自アンエイリアスきった状態。FireFoxだと結構綺麗で、ChromeOperaだと線が細すぎ。
http://jgame-js.sourceforge.jp/examples-2.0/adventure2.html
多分だけど、これを許可するのがベターなんだと思う。

複数行のテキストはサポートされてない件

サポートされてないから自分で作るしかない。
一番簡単な実装はこうだね。

var context = canvas.getContext("2d");
var text = "abcdefghijklmnopqrstuvwxyz";
var offset = {x: 0, y: 0}
for (var i=0; i<text.length; i++) {
 var c = text.substr(i, 1);
 var metric = context.measureText(c);
 offset.x += metric.width;
 if (offset.x > rowWidth) {
  offset.y += rowHeight;
  offset.x = 0;
 }
 context.drawText(...
}

※注:ちょっと適当。rowWidthとrowHeightとcanvasは上記スクリプト内に存在してないし。


まあ、measureTextで一文字ずつ文字幅調べてって、行の幅を超えたら改行。
今回はほぼそれでやった。おっそいんだけどね。

その他の問題

これはCANVAS使いなら常識なのか知らんけど、行の高さのとりかたに苦労した。


http://www.html5.jp/canvas/ref/property/font.html
に、よると、line-heightは常時normalみたい?


line-heightのnormalってのは、
https://developer.mozilla.org/ja/docs/CSS/line-height
に、よると、1.2みたい?

line-height = 1.2ってのはフォントサイズの1.2倍という意味なので、フォントサイズが18pxであれば 18px * 1.2 = 21.6pxみたい?


でも小数点座標だと時々フォントがにじんだりするみたいだから、Math.round(18px * 1.2) = 22pxとするのが正解っぽい?


などと試行錯誤をした結果今の形になった。
でもなんかバグあるかも。特定端末で動かないとか、実はline-heightもちゃんと指定できるよとか。

jgame.js側での実装について軽く

MultilineTextっていう複数行の文字列を扱うためのクラスがあって、これの中身はSpriteとクリッピング領域用のLine。
最初に裏側で全部書いてから、Lineのクリッピング領域を広げることで見せる範囲を変えてる。


これだけだとRPGとかで使いづらいので、さらにそれをラップしたMessageWindowってのも作った。
これは外側の枠、内側の枠、MultilineText、続きがある場合のカーソル、の合計4つの部品で出来てる。


外側の枠を画像(Sprite)に変えればデザインされたウィンドウになるだろうし、まあカスタマイズは適当にしてくれよと。
それなりに使いやすくしたつもりだけど、結局この手のものって実運用してみないとはっきりしたことわかんないからね。
再来週頭くらいに不思議のすごろくの再開をするので、そこで使っていってぼちぼち調整していく予定。

まとめ

  • 実装結構大変。
  • フォントが汚い問題はまだ残ってて、多分独自で影つけたりするのはやめるしかない気がする
  • 使いやすく作ったつもりだけど、実運用は再来週頭予定

もしかしたら、素直にCANVASの上にposition:absoluteなDIVでもおいた方がずっと綺麗に出て速度も安定するのかもね。速度面はテストしてないけどさ。