2010-12-10
HTML5のDrag and DropとFileAPI、画像のトリミング操作

Demoページ: MICMS: Drag and DropとFileAPIを利用した画像ファイル登録
Drag and Drop API
以前ブログ記事としても書いた、Blogaraに実装したDrag and Drop操作。 あれは単にUIの機能として試しに使ってみたというだけのもので、JavaScriptによる実装やチェックボックスが並ぶ伝統的?なUIで代替可能な機能であった。
そこで今回は、HTML5のDrag and Drop(以下DnD)APIとFileAPIを利用する事で実現出来るブラウザ外から来るファイル情報を扱う処理を実装する。
MICMS
今回のDnDAPIとFileAPIを利用したファイル情報の取得を実装する発端になったのは、 現在当社で開発中のCMSであるMICMS(Multipurpose Integrated Content Management System)で、 Webサイト上での画像ファイル管理(主に登録)がより簡易にならないか、という要望があった事が契機。
コンテンツ販売系のECサイトなどでは、パッケージ画像のように表示する用途によって同一デザインの画像でも数種類のサイズのバリエーションが必要となるケースがあるが、 一々それらのファイルを作成し登録する作業はそれなりのコストが掛かる。
そこで、1つの元になる画像を管理ページにDnDし、その画像からサイズ違いの画像を生成。 さらに、要求されるサイズやアスペクト比とは異なる画像がDnDされた場合にはどのような処理を行うかのオプション選択を可能にし、それに従った画像を生成する。というような機能を実装する事にした。
画像データの生成
元となる画像は、DnDされたファイルをFileReaderAPIで読み込みdataURL形式の文字列として取得出来るが、 リサイズ後の画像データはどのように生成し登録するかというのが最初の検討ポイント。
とは言えこの機能の開発開始時点では、HTML5のcanvasで生成した画像をtoDataURL()によりdataURL形式で取得出来るとは知らなかった為に、 画像生成に関しては当然のようにサーバー側(PHPのgd)で行う事を前提としていた。
という訳で、今回のDnDAPIとFileAPIを利用した画像の登録処理としては、
- DnDされた画像データをdataURL形式で取得
- 指定のサイズ(解像度)/アスペクト比と異なる場合のオプション表示
- サーバー側にdataURL形式の画像データと共にオプション設定をAjax送信
- サーバー側のgdでは、画像データとオプション設定を元に画像を生成しファイルやDBに保存、さらにAjaxのレスポンスとして生成した画像データをdataURL形式で返す
- クライアント側は生成された画像を登録画像として表示し確認
のような流れを想定した。
DnDとFileAPIを利用したファイル情報の取得
DnDとFileAPIと利用したファイル情報の取得についての基本は、既にネット上に多くの親切ドキュメントが存在するのでここでの説明は割愛。 HTML5 Drag and Drop and File API Tutorial | The Buzz Mediaや Using files from web applications - MDC Doc Center などのページをご覧あれ。
画像ファイルの場合の特殊処理
では基本的な部分以外の処理として何を行う必要があるかとなると、画像ファイルに特有の処理が考えられる。
画像ファイルと一言で言っても、Webで一般的なPNG,JPEG,GIFなどの他にもTIFFやらBMPやらTGAなど膨大な種類のデータが存在する為に、 処理対象として適切なフォーマットであるか否かが重要となる。
特に今回は、サーバー側での画像変換/生成となる為にPHPのgdでサポートされている形式である必要がある。
ファイル拡張子がブラウザで理解出来るものならば、dataTransfer.filesの各File objectのtypeには拡張子を元にしたらしいMIMEタイプが格納されている。
そこでそれぞれのファイルが拡張子により"自称"するMIMEタイプに対するチェックを行い、
要求する形式とは異なる場合にはエラー扱いとしている。

が、これは結局自称レベルの信憑性しか無いので、正しいヘッダと正しいデータを持つ適切な画像ファイルか否かまでは判らない。
そこで次の段階のチェック。 FileReader経由で画像の実データが取得出来るので、一般のアプリケーションのようにバイナリデータ中の情報を読み判定する事も可能だが、実際のところ手間が掛かる。
という訳でここはブラウザに任せ、正しい画像データとして表示されているらしい状態のものを適切な画像データとして扱う事にし、
正常な画像として扱われているか否かとしては、画像の幅(width)と高さ(height)が0では無いかどうかを判定材料にする。

この例は、CSSファイルの拡張子を.jpgに変更しただけの無効な画像ファイルをDnDした結果。
画像のサイズ(解像度)のチェックと画像加工オプション指定
正常な画像であろう事が確認出来た後は、画像のサイズやアスペクト比が要求するものと一致するかのチェックを行う。
それらの条件を満たしているならそのまま登録可能となり、dataURLをサーバーに送り、サーバー側で画像データの保存を行う。
一方、サイズ/アスペクト比が異なっている場合には単なるエラー表示ではなく、
ユーザーが何らかの対応を行える事が望ましい。

という訳で初期の実装では以下の選択が可能となっていた。
- そのまま登録
- 文字通り、特に加工修正指示も無くこのままのデータをサーバーに送る。
- アスペクト比を保ったままリサイズ
- 元画像のアスペクト比(幅と高さの比率)はそのままに、要求するサイズに元画像が収まるように必要があれば縮小/拡大する。アスペクト比が異なる場合には、作成後画像には余白が生まれる。
- 指定のサイズに強制リサイズ
- 元画像のアスペクト比を無視し、要求するサイズに縮小/拡大する。アスペクト比が異なる場合には変形するので人物/風景画像には不向き。
これらのオプションは特に詳細な設定も必要無く、サーバー側での画像生成時にサイズ計算を行えば良いだけなので簡単な処理となる。
トリミング
最初に書いたように、本来この機能は同一デザインでサイズ違いの画像を簡易に生成する事が目的だった為に、画像加工的なUIは必要では無かった。
が、ついでの手間と共にブログCMSにクライアント側でのトリミングなどの操作が可能なプラグインが存在すると小耳に挟み、その対抗?の意識から今回の機能に追加してみようと目論んだ。
トリミング(Cropping)と言っても今回は矩形範囲を指定するだけなので、サーバー側の処理には大した変更は必要無い。 一方、クライアント側ではトリミング範囲を指定する為のUIが必要となる為に、それなりの追加作業を行う事になる。
トリミング範囲指定の為のUIとして、最も単純なのは左上と右下の座標を記入するinput type=textを用意しそこに数値を入力してもらう形となるが、それでは余りにも面倒であり直感的にも判り辛い。
そこで、一般のレタッチソフトのように画面上で矩形範囲を確認しトリミングを指定、同時にその結果が別Windowに表示されるようなUIを想定した。
トリミング範囲指定
まずトリミング範囲指定に必要とされる要素とその実装案としては、
- 矩形範囲の表示
- borderやCSS3のopacityを利用し枠だけの矩形や半透明の矩形を表示する。
- 矩形範囲の移動
- 矩形範囲にmousedownイベント、矩形移動可能範囲にmousemoveイベントを設定し、一種のマウスキャプチャ処理を行う。
- 矩形範囲の拡大/縮小
- mousewheelの回転イベントから行う。キーボード操作も可能にした方が便利だが、とりあえず今回は保留。
トリミング画像表示
今回は単にある部分を切り取るだけのトリミングでは無く、画像サイズ指定に合わせる為に縮小/拡大も行う。
縮小/拡大を行わないのなら、元画像の矩形範囲=作成画像となるので特に作成後画像を表示する必要も無いが、 リサイズを行う場合には作成後画像の実寸表示をする事が望ましい。
そこで、作成後画像表示を行う子Window(ダイアログ? というかWebアプリ界での表現は不明)を作成し、そこに表示を行うようにする。 作成後画像表示Windowに必要な要素は、
- タイトルバーとWindowの移動
- 各種WindowGUIのように簡単な説明が書かれるタイトルバーとそれをDragしてのWindow移動が可能。
- 表示画像の更新
- 矩形範囲の移動/縮小/拡大が行われた場合には、それらの操作をリアルタイムに反映。
- コントロールUIの設置
- 操作結果を反映するかのOk/Cancelボタンや、Windowを非表示にするCloseボタンを設置する。
このような矩形範囲と作成後画像表示の機能を実装したものが、

実際に利用可能なサンプルページとして、
MICMS: Drag and DropとFileAPIを利用した画像ファイル登録
canvas化へ
上記の作成後画像表示で検討課題となったのは、どのような方法で表示するかという点。
候補となったのは以下、
- image element (採用プラン)
-
image elementを作成し、そのsrcにDnDされたdataURLを設定。
表示範囲にはoverflow:hiddenを設定し、指定サイズ外の部分を非表示にする。
表示範囲の親elementにposition:relativeを設定し、表示範囲はposition:absolute。
移動が行われた場合はCSSのtopとleftを変更し、表示位置を調整。
縮小/拡大が行われた場合は、imageのwidthとheightを変更し、サイズ変更分の表示位置修正も行う。
デメリットとしては元画像が巨大だった場合や拡大比率が大きい場合には、作成後画像表示で大部分が非表示の巨大画像を扱う事になる点。 - CSSのbackground指定
- CSS Spritesの手法で知られるように表示位置の設定が出来るので移動は可能。が、縮小/拡大が可能かは不明。
- CSS3の各種エフェクト
- CSS3の策定案にありブラウザで先行実装が始っている機能を利用。出来そうだがCSS3はまだ利用していないので不明。
- HTML5のcanvas
- 画像の操作といえばcanvas。とは言え未だに未着手なので不明。
このように、結局今回は言うなれば古い手法である画像エレメントのwidth/height属性を指定しての縮小/拡大とtop/leftでの移動による実装を行ったが、 どうせHTML5のDnDとFileAPIが必須ならば、次の段階としてついでにcanvas化も行おうというという事で続く…
参考資料
HTML5 Drag and Drop and File API Tutorial | The Buzz MediaUsing files from web applications - MDC Doc Center