クロスバックエンドな吹きだし表示機能

ぺったんRの吹きだしは、さまざまな尻尾の向きを持つ吹きだし画像を用意します.
また、吹き出しが巨大になったときに、吹きだしの枠線も太ってしまわないように、枠線のバリエーションも複数用意します.

これら画像の転送を抑え、より美しく吹き出しを表示する技術があります。SVG をはじめとしたブラウザ上でベクター画像を描く仕組みです。 ぺったんR はベクター画像機能を活用して、ユーザーエクスペリエンスをあげ↑あげ↑します。

関連 ClientBalloonEditor

吹きだし画像の転送量・概算

吹きだし画像は gif で注意深く用意されるため一枚につき、数キロバイトという軽量なものです.しかしコマの編集時にはユーザーが操作している間にそのすべてのバリエーションを読み込んでしまう事態になります.
マンガという表現スタイルを考えると、キャラクター画像一枚に対して、ひとつ程度の吹き出しが登場する、と仮定してみます.すると、ぺったんのページ読み込み時の全画像アクセスのうち、半分を吹きだし画像が締めることになる試算です.
(もちろん、キャラクター画像の方がはるかにファイルサイズが大きくなります.注意深く容量を抑えて作られた場合でも 10KB ~ 20KB になり、吹きだし画像の数倍以上のサイズになります.)

このような、吹きだし画像へのアクセスを抑止して、サービスの安定を図るためベクター画像をサポートするブラウザについては、ブラウザ側で吹きだし画像を用意します.

SVG を基本に

ベクター画像を描く技術は複数有り、各ブラウザはそのひとつ、または複数をサポートしたり、しなかったりします。

では、バルーン作家はどうすればいいのでしょう?バルーン作家は SVG だけを使用し、そのほかの技術に注意を払う必要はありません。

バルーン作家がバルーン用のデータとスクリプトを作成し、その際にバルーン作家は SVG のためのスクリプトを書きます。

ブラウザが SVG をサポートしない場合、Canvas, VML, Flash といった手段でフキダシを描画しますがこれは pettanR ライブラリ側の仕事です。

pettanR 基本スクリプトは、SVG を適宜に解釈して Canvas 等で描画する。といっても SVG の全ての機能を網羅するのはキツイので、とりあえず path だけを考えています。

SVG のパスを VML のパスに変換

大体似た感じなんだけど、実は結構違うこの二つ。VML のほうが使い勝手が悪い気がする。

楕円弧の処理に対応できていない! ズバリな js ツールがあったのでこれを見て研究します。IE8 で動作確認。HELP を開くとサンプルのSVGをコピペできる。何か操作をすると vml 変換される。

http://ayanaotakashun.com/sample/svg_vml/svg_vml_d.htm

以下は現在の VML / SNG 切り分け部分、、、

  1. if( IS_VML === true ){
  2. var _tailX = tailX *10,
  3. _tailY = tailY *10,
  4. __w = this.w *10,
  5. __h = this.h *10;
  6. this.path.style.width = this.w + 'px';
  7. this.path.style.height = this.h + 'px';
  8. this.path.coordsize = [ __w, __h ].join( l );
  9. this.path.path = [
  10. ' ar ', 0, l, 0, l, __w, l, __h, l,
  11. round( endX * 10 ), l, round( endY * 10 ), l,
  12. round( startX * 10 ), l, round( startY * 10 ),
  13. ' l ', round( _tailX ), l, round( _tailY ),
  14. ' x e'
  15. ].join( '' );
  16. this.elm.style.marginTop = _tailY < 0 ? floor( ( 60 + _tailY) / 10 ) : 10;
  17. this.elm.style.marginLeft = _tailX < 0 ? floor( ( 60 + _tailX) / 10 ) : 10;
  18. } else {
  19. this.elm.setAttribute( 'width', this.w + PADDING_LEFT *2 );
  20. this.elm.setAttribute( 'height', this.h + PADDING_TOP *2 );
  21. this.path.setAttribute( 'd', [
  22. 'M', cround( tailX + PADDING_LEFT ), l, cround( tailY + PADDING_TOP ),
  23. 'L', cround( startX + PADDING_LEFT ), l, cround( startY + PADDING_TOP ),
  24. 'A', rx, l, ry,
  25. '0 1 1', // flag
  26. cround( endX + PADDING_LEFT ), l, cround( endY + PADDING_TOP ),
  27. 'z'
  28. ].join( ' '));
  29. }

文献

Scalable Vector Graphics (SVG) 1.1 (第2版)

http://www.hcn.zaq.ne.jp/___/SVG11-2nd/index.html#minitoc

SVG学習辞典

http://www.hcn.zaq.ne.jp/___/SVG11-2nd/paths.html#PathData

ベクトルマークアップ言語(VML) http://www.doraneko.org/misc/vml/19980513/Overview.html#_Toc416858391

http://defghi1977-onblog.blogspot.jp/search/label/独学svg

移動 と 終了

ライン

2次ベジェ曲線

3次ベジェ曲線

楕円弧

VML と SVG で楕円弧の指示方法が異なるが、変換できそうな感触。しかし、よく見てみると VML には楕円の回転角度を扱う術がない。

SVG / VML 名前 コマンド パラメータ数 1 2 3 4 5 6 7 8  解説
SVG 楕円弧 A 7 横方向の半径 縦方向の半径 楕円の回転角度 弧の大きさフラグ 1.長い弧 0.短い弧 弧の向きのフラグ 1 角度が正の向き 0 負の向き 座標x 座標y 絶対座標
SVG 楕円弧 a 7 横方向の半径 縦方向の半径 楕円の回転角度 弧の大きさフラグ 1.長い弧 0.短い弧 弧の向きのフラグ 1 角度が正の向き 0 負の向き 座標x 座標y 相対座標
VML 楕円弧 at 8 left top right bottom 開始点x 開始点y 終了点x 終了点y 弧の開始点へ現在点から直線が描画。弧は反時計回り方向に描画。
VML 楕円弧 ar 8 left top right bottom 開始点x 開始点y 終了点x 終了点y 弧の開始点への黙示的な moveto により新しいサブパスが開始される。
VML 楕円弧 wa 8 left top right bottom 開始点x 開始点y 終了点x 終了点y at と同じであるが、弧が時計回り方向に描画。
VML 楕円弧 wr 8 left top right bottom 開始点x 開始点y 終了点x 終了点y ar と同じであるが、弧が時計回り方向に描画。

この場合傾いた楕円弧に近似するベジェ曲線で表すことになりそう。

AS3で楕円弧を描く

http://d.hatena.ne.jp/flashrod/20061108/1162996851

楕円弧の扱いはとても厄介みたい、でも吹き出しでも使ってる。(楕円の回転角度はない)

以下のコードは楕円の回転角度も扱っている。魔法のようなコードが並んでいて、この分野が半端ない、ということは分かる、、、

svgのpath文字列をスクリプトで直接変形する

http://defghi1977-onblog.blogspot.jp/2013/03/svgpath.html

むむむ、、、SVG Basic と SVG Tiny では楕円弧がそもそもサポートされない、、、

http://www.hcn.zaq.ne.jp/___/REC-SVG11-20030114/mobile-profile/index.html#sec-shapes

SVGB(asic PDA用) と SVGT(iny 携帯機器用) は楕円弧命令("A"(絶対)と "a"(相対))を除く全ての SVG 1.1 のパス命令をサポートする。

結論

  • SVG ⇒ VML では、 SVG に楕円の回転角度指定のある(0, 90, 180, 270 以外)楕円弧が使われている場合、ベジェ曲線を使って楕円弧を描く。
  • SVG ⇒ SVG というコードも必要なケースがあり、SVG Tiny または SVG Basic な端末の場合、楕円弧の部分をベジェ曲線に置き換える。

SIE というライブラリが 楕円弧をベジェ曲線に変換している。

http://d.hatena.ne.jp/dhrname/20070712/1184250233

Arcオブジェクトを作るため。これは、path要素の弧を作り出すコマンドArcToの値を、ベジェ曲線の値に変換することが可能です。

http://sourceforge.jp/projects/sie/scm/git/sie/blobs/master/org/sie-uncompressed.js

SVG を SVG(Tiny, Basic) に変換

バルーン作家のスクリプトはパスで楕円弧を使えるが、 SVG Tiny または SVG Basic までしかサポートしないブラウザのために、楕円弧部分をベジェ曲線に置き換えるコードを pettanR ライブラリが提供する。

ブラウザ機能対応表

バックエンドie5~8ie9+FFsafarichromeOperaAndroid 標準ブラウザー(webkit) iOS mobile Safari
VML?××××××
SVG×1.5+3.0+1+8+3+
flash3-×
Canvas×??1+8?

サーバ側吹きだし画像(system_pictures)を使用する場合の計算式

以下は common.js の吹きだし画像の決定部分.

  • _a は 吹きだしのしっぽの角度 0 ~ 360 °
  • NUM_BALLOON_IMAGE は サーバ側で用意している吹きだし画像の角度のバリエーション.仮にこれが 36枚だと、一枚の画像が 10°を担当することになる.
  • しっぽは真上をむいているときに、0°とする.これが 最初の画像に相当する.-5°~5°の範囲の場合、この画像の出番となる.
    function balloonUrlBuilder( _a){
    	var d = 360 /NUM_BALLOON_IMAGE;
    	_a += d /2;
    	return [ 'system_pictures/_w', _a < 360 -d /2 ? floor( _a /d) : 0, '.gif'].join( '');
    }
    

基本はこれでいいんですけど、このままじゃ360を割り切れるNUM_BALLOON_IMAGEでなきゃならないので辛いですね。例えば、4の場合は、90置きになって上下左右しか作れず、左上から90置きの四隅配置なんかはできない。オフセット値を用意して、0が上ではなく、起点を変えられるようにしたいところ。 yas 2012/02/07

起点が真上って仕様は不利。しっぽはキャラクターの口元に向かう。尻尾真上=フキダシ真下ってこと。キャラクターは大抵コマの中央にあるので、それを潰すようにコマ下部にフキダシを載せることは考えづらい。となると、ユーザは必ず尻尾を調整するハメになる。横書きは左上から右下に向かって読むから、おそらくフキダシは左上が多く、尻尾は右下が多いはず。詳しくはデータを取らないとわからないが、こういうのは頻度が高く、かかる手数が少ない位置をデフォルトにしてやるべき。ってことで、やはりオフセット値は必要。 yas 2012/02/07

しっぽのデフォルト向きの件、同意です。下側が良さそう。 しっぽの角度は ビルトインな UI をライブラリが提供するので、他のバルーン作家が作ったバルーンもこれに倣うはずです。itozyun 2013/03/23

仮に、キャラクターを右クリック(できない端末ではなんらかの手段でコンテキストメニューを出してから)、このキャラクターにフキダシを追加 すると、吹き出しの尻尾は、吹き出しの座標とキャラクターの座標から自動で設定される。ユーザーが吹き出しの角度をいじると、この自動追尾機能は off になる。このメリットは単にユーザーの手間をいくらか省ける、だけえはなくて、吹き出しとその話し手を関連付けることができる。吹き出し要素に話者を指す情報を持たせて話者とフキダシを関連付けておくとあとあと何かに使えるかもしれない。 itozyun 2013/4/29