maesblog

Adobe Creative SDKで作るWebフォトエディタにHTML5のFile APIとDrag & Drop APIで画像を取得する機能を追加

先日SNSなどを中心に話題となっていた『わずか数行のコードで、Adobe製の画像編集エディタを無料で使い放題にできる「Creative SDK」がヤバイ! | APPGIGA!!』という記事がありました。この記事によって、初めて「Adobe Creative SDK」の存在を知りました。実際に試してみたところ、かなり優秀なSDKであるということがわかりました。自由にWeb上に画像を取り込めるようにしたら、けっこう使えるWebアプリが作れると思ったので、APPGIGAでは紹介されていなかった画像の取り込み部分を中心に今回は実装方法を紹介させていただきます。

Adobe_Creative_SDK
Adobe Creative SDK

Adobe Creative SDKで作ったフォトエディタ

Adobe Creative SDKを使って作ったフォトエディタです。使い方は簡単です。まず画像をドラッグ&ドロップまたは「参照」ボタンを押してページ内に取り込みます取り込んだ画像をクリックするとフォトエディタが立ち上がります。このフォトエディタが驚くほどに高機能です。まずはお試しください(処理は全てブラウザ内で行われます)。

ここに画像ファイルをドラッグ
(複数可能)
[ png / jpg / gif ]

フォトエディタサンプル(取り込んだ画像をクリックすると編集画面が開く)

このサンプルのソースは以下で確認できます。

Adobe Creative SDK

サンプルを見ていただいたらわかると思いますが、画像編集機能がこれでもかというくらい充実しています。これを実現しているのが「Adobe Creative SDK」となります。画像編集部分は一切自分では実装していません。以下をページに読み込むだけで簡単に画像編集機能を盛り込むことができてしまいます。

<script src="http://feather.aviary.com/imaging/v2/editor.js"></script>

そしてAPIを使う際に気になる点といえば「制限」に関する部分となりますが、Adobe Creative SDKの利用条件を読むと、以下のようになっています。通常1秒間に何リクエストといった制限が設けられていたりしますが、このAdobe Creative SDKに関しては、ある程度自由に使えるようになっています。

利用の制限 コール数がAPIまたは本サービスに悪影響を及ぼすと弊社が確信した場合、弊社はAPIが受け取るコールの数を制限する場合があります。

Adobe Creative SDKの使い方などは、本家サイトのドキュメントやAPPGIGAの記事などを見ていただければと思います。

今回利用するHTML5のAPI – File API / DnD API

「APPGIGA!!」の記事では、画像を取り込む部分の実装がなかったので、当ブログでは画像を取り込む部分をメインに実装方法を紹介していきたいと思います。

今回のサンプルでは、ドラッグアンドドロップまたは「参照」ボタンを押して画像を選択して取り込むようにしています。これらの機能を実装するにあたって、以下のHTML5のAPIを利用しました。

File API

File APIは、「Webアプリにおいてファイルオブジェクトを表現するためのAPI」です。プログラムによるファイルの選択とそのデータへのアクセスを可能にします。

今回の実装では、File APIの中の以下のインターフェースを使用しました。

Fileインターフェース
ファイル名や更新日などのファイルに関する情報(読み取り専用)を提供するインターフェースです。
FileListインターフェース
<input type=”file”>やドラッグ&ドロップなどを通して選択されたそれぞれのファイルの配列を提供するインターフェースです。
FileReaderインターフェース
FileやBlobを読み込むためのメソッドで、読み込んだ結果を取得するためのイベントモデルを提供するインターフェースです。

Drag and Drop API

Drag and Drop APIは、「ページ内でドラッグ&ドラッグによる操作を実現するためのAPI」です。

APIの使用可否判定

ブラウザがこれらのAPIに完全に対応しているかどうか判定する場合は、以下のように書きます。こうすることで、ブラウザがAPIに対応している場合と対応していない場合の処理を書き分けることができます(以下は今回使用した機能での判定方法となります)。

File APIの使用可否判定

if (window.File && window.FileReader && window.FileList) {
  // ブラウザがFile APIに対応してる場合の処理を書く
} else {
  // ブラウザがFile APIに対応していない場合の処理を書く
}

Drag and Drop APIの使用可否判定

if ("ondrop" in window && "ondragover" in window) {
  // ブラウザがDrag and drop APIに対応してる場合の処理を書く
} else {
  // ブラウザがDrag and drop APIに対応していない場合の処理を書く
}

ドラッグアンドドロップで画像をサイトに取り込む

それでは、実装の方法を紹介していきます。まずはドラッグ&ドロップで画像をサイトに取り込む方法から説明します。上で触れたHTML5のFile APIとDrag and Drop APIで実装します。

画像をドラッグするエリアを作成

最初にドラッグする領域を作っておきます。空のdiv要素でも良いです。CSSで幅と高さをつけておくと良いでしょう。

<div id="cb-drag">
  <p>ここに画像ファイルをドラッグ<br>(複数可能)<br>[ png / jpg / gif ]</p>
</div>

イベントリスナーを設定

先ほど作ったドラッグ領域に画像をドラッグした時に処理を実行させるようにします。ここでDnD APIの「drop」イベントと「dragover」イベントをイベントタイプとして使います。dropイベントが発生した際に、この後説明するreadImage()関数を実行するようにしています。ドラッグ中は何も処理をさせないようにします。

// ドラッグ領域を取得し、drag変数に代入
var drag = document.getElementById("cb-drag");

// イベントリスナー用のdragFile()関数を定義
function dragFiles() {

  // drag変数をターゲットにdropイベントが発生したら、function()を実行
  drag.addEventListener("drop", function (e) {
    e.stopPropagation();
    e.preventDefault();
    // readImage()関数を実行
    readImage(e);
  }, false);

  // drag変数をターゲットにdragoverイベントが発生したら、function()を実行
  drag.addEventListener("dragover", function (e) {
    e.stopPropagation();
    e.preventDefault();
  }, false);
}

stopPropagation()preventDefault()というメソッドを使っていますが、これは画像をブラウザ上に置いた時に画面が切り替わってしまうのを塞ぐためです。詳細は以下をご確認ください。

画像リストを取得

ドラッグアンドドロップされたファイルは、DataTransferオブジェクトに保存されます。保存されたデータはイベントのdataTransferプロパティを介して取得することができます。

ドラッグした画像リストを取得する関数を以下のように実装します。

// 画像リストを取得するreadImage()関数を定義
function readImage(e) {
  // dataTransferプロパティを介して画像リストを取得し、f変数に代入
  var f = e.dataTransfer.files;
  // 取得した画像リストを画像の数だけループ処理させる
  for (var i = 0, l = f.length; i < l; i++) {
    /*
      ループ処理を書く(後ほど説明)
    */
  }
}

フォームから参照して画像をサイトに取り込む

ドラッグアンドドロップだけでなく、ファイルを参照して取り込めるようにしておくと使い勝手もよくなると思います。次にフォームから参照して画像をサイトに取り込む方法を説明します。

画像を参照するボタンを作成

画像をローカルから参照して取り込むにはinput要素のtype属性に「file」を指定します。複数のファイルを選択できるようにするには、input要素の中にmultiple属性をつけておきます。

<input type="file" id="cb-file" multiple>

イベントリスナーを設定

ファイルを選択するとchangeイベントが発生します。このchangeイベントが発生した時にreadImage()関数が実行されるようにイベントリスナーを設定します。

// 参照ボタンを取得し、file変数に代入
var file = document.getElementById("cb-file");

// イベントリスナー用のuploadFile()関数を定義
function uploadFiles() {

  // file変数をターゲットにchangeイベントが発生したら、function()を実行
  file.addEventListener("change", function(e) {
    // readImage()関数を実行
    readImage(e);
  }, false);
}

画像リストを取得

<input type="file">から選択されたファイルは、イベントのtargetプロパティを介して取得することができます。選択した画像リストを取得する関数を以下のように実装します。

// 画像リストを取得するreadImage()関数を定義
function readImage(e) {
  // targetプロパティを介して画像リストを取得し、f変数に代入
  var f = e.target.files;
  // 取得した画像リストを画像の数だけループ処理させる
  for (var i = 0, l = f.length; i < l; i++) {
    /*
      ループ処理を書く(後ほど説明)
    */
  }
}

取り込んだ画像を表示する

画像リストを取得できるようになったので、今度は取得した画像リストを実際にページに表示させるようにしていきます。画像を取り込む方法はドラッグ&ドロップと、<input type="file">の2通りありましたが、ここは共通の処理で実装します。

取得した画像リストに対してループ処理

取り込んだ画像が複数あった場合は、それぞれに対して処理を実行していく必要があるので、ループさせて処理させていきます。ここではFile APIのFielReaderインターフェースを使って取り込んだ画像をメモリに保存し、ブラウザ上で扱えるようにしていきます。

取り込んだ画像をページに表示できる状態にする関数を以下のように実装します。

// readImage()関数を定義
function readImage(e) {

  // dataTransferプロパティ(DnDの場合)、またはtargetプロパティ(参照の場合)を
 // 介して画像リストを取得し、f変数に代入
  var f = (e.dataTransfer) ? e.dataTransfer.files : e.target.files;

  // 取得した画像リストを画像の数だけループ処理させる
  for (var i = 0, l = f.length; i < l; i++) {

    // FileReader()をnewしてreaderインスタンスを作成
    var reader = new FileReader();
    // FileReaderによるメモリへの読み込みが終了した時点でfunctionを実行
    reader.onload = (function (f) {

      // imageDataオブジェクトを作成
      var imageData = {};
      // 実際の処理内容
      return function (evt) {
        // 取り込んだファイルタイプをチェック。gif、png、jpegだった場合
        if (f.type === "image/gif" || f.type === "image/png" || f.type === "image/jpeg") {
          // imageDataオブジェクトに取得した画像情報をセット
          imageData.type = f.type; // ファイルタイプ
          imageData.name = f.name; // ファイル名
          imageData.size = f.size; // ファイルサイズ
          imageData.date = f.lastModifiedDate.toLocaleDateString(); // 最終更新日
          imageData.url = evt.target.result; // ファイルのURL
          // ファイル情報をセットしたimageDataオブジェクトを引数にセットし、
          // makeView()関数を実行
          makeView(imageData);

        // 取り込んだファイルが画像でなかった場合、処理終了
        } else {
          return;
        }
      };
    })(f[i]);

    // img要素のsrc属性に指定して画象として表示できるように
    // ファイルオブジェクトをData URIに変換
    reader.readAsDataURL(f[i]);
  }
}

画像を表示させるViewを作成

上記で取り込んだファイルをブラウザで扱えるようにしました。そのデータを実際にページに表示させます。上のコードでmakeView()という関数を指定していた箇所があったかと思いますが、そのmakeView()関数を使ってページ上に画像を表示させるようにします。

実装方法は以下の通りです。ここはほとんどDOMの操作のみとなります。ポイントは関数の引数に取得した画像情報のオブジェクトを渡しているところです。それから予め画像表示領域をdiv要素なりで作っておく必要があります(ここではdiv要素のidに「cb-display」と指定)。

// 画像表示用のmakeView()関数を定義
function makeView(data) {

  // div変数、img変数、cutomEvent変数を初期化
  var div, img, customEvent;
  // 画像表示領域を取得し、drag変数に代入
  var disp = document.getElementById("cb-display");
  // dataオブジェクトから取得できる画像情報の表示用のHTMLをmetaData変数に代入
  var metaData = "<p>■ファイル名: <b>" + data.name + "</b><br>■容量: <b>" + data.size + "</b>バイト</p>";

  // div要素を作成
  div = document.createElement("div");
  // div要素のclass属性にcb-divを指定
  div.setAttribute("class", "cb-div");

  // img要素を作成
  img = document.createElement("img");
  // img要素のsrc属性に画像のURLを指定
  img.src = data.url;
  // img要素のclass属性にcb-imageを指定
  img.setAttribute("class", "cb-image");
  // img要素のstyle属性にmax-width: 100%を指定(レスポンシブ対応用)
  img.style.maxWidth = "100%";
  // img要素のstyle属性にheight: autoを指定(レスポンシブ対応用)
  img.style.height = "auto";

  // div要素にimg要素を追加
  div.appendChild(img);
  // img要素の後にmetaData(画像情報表示用HTML)を追加
  img.insertAdjacentHTML("afterend", metaData);
  // 画像表示領域dispにdiv要素を追加
  disp.appendChild(div);

  /* == 以下の部分は後ほど説明します == */
  // イベントを作成し、cutomEvent変数に代入
  customEvent = document.createEvent("HTMLEvents");
  // makeViewというイベント名でcustomEvent変数を初期化
  customEvent.initEvent("makeView", true, false);
  // makeViewイベントを発火
  div.dispatchEvent(customEvent);
}

表示した画像をクリックすると編集画面が開くようにする

取り込んだ画像をページ上に表示させられるようになりました。最後に表示した画像をAdobe Creative SDKを使って編集できるようにします。画像をクリックした時に、画像編集画面が表示させるようにします。画像にはmakeView()関数により「cb-image」というclass属性を付与しています。

カスタムイベントを受け取り、画像を再取得し、ループ処理を再調整

上のコードでカスタムイベントを設定しました。maekView()関数によりviewが生成されたら、「makeView」というイベントが発火するようになっています。ここでは、「makeView」が発火したら、表示される画像を再取得し、ループの回数を再調整させるようにしています

表示した画像をクリックすると編集画面が開く関数を以下のように実装します。

// listener()関数を定義
function listener() {

  // 画像表示領域を取得し、drag変数に代入
  var disp = document.getElementById("cb-display");
  // disp変数をターゲットにmakeViewイベントが発生したら、function ()を実行
  disp.addEventListener("makeView", function () {

    // 表示された画像を取得し、image変数に代入
    var image = document.querySelectorAll(".cb-image");
    // 表示されている画像の数だけループ処理を実行
    for (var i = 0, l = image.length; i < l; i++) {
      // 表示されている画像に対してid属性を付与(Adobe Creative SDK用)
      image[i].setAttribute("id", "cb-image_" + i);
      // 表示されている画像がクリックされたら、editPhoto()関数を実行
      image[i].addEventListener("click", editPhoto, false);
    }
  }, false);
}

Adobe Creative SDKを使って画像編集画面を表示

上のコードで、画像をクリックしたらeditPhoto()関数を実行するようにしました。editPhoto()関数には、Adobe Creative SDKを使って画像編集画面を開く処理を書いていきます。詳細はAPPGIGA!!の記事などを見ていただければと思います。

// Aviaryオブジェクトをnewして、featherEditorインスタンスを作成
var featherEditor = new Aviary.Feather({
  // Adobe Creative SDKのAPIKEYを指定
  apiKey: "XXXXXXXXXXXXXX",
  // 対象の画像のidとurlを指定(Save用)
  onSave: function(imageID, newURL) {
    var img = document.getElementById(imageID);
    img.src = newURL;
  }
});

// editPhoto()関数を定義
function editPhoto() {
  // 画像のid属性を取得してid変数に代入
  var id = this.getAttribute("id");
  // 画像のsrc属性を取得してsrc変数に代入
  var src = this.getAttribute("src");
  // featherEditorインスタンスのlaunchメソッドを実行
  featherEditor.launch({
    image: id,
    url: src
  });
  return false;
}

これで画像をクリックしたら画像編集画面が表示されるようになります。その他に、サンプルでは表示した画像を削除するボタンなども実装していますが、説明が複雑になってくるので省略します。以下にソースをアップしておきましたので、そちらを見ていただければと思います。

まとめ

自分はよく写真を撮ったりして、レタッチなんかもよく行ったりするので、画像編集に関してはけっこう興味があったりします。以前こちらのブログで、『CSS filterプロパティとFilter Effectsの話』という記事を書きました。CSSのFilterプロパティを使って、画像編集を実現しようという話ではありましたが、CSSで編集した画像は保存が簡単にはできないという問題がありました。

それに対して、Adobe Creative SDKはかなり使えますね。編集機能も協力だし、保存もできます。まだまだ機能も追加していけそうなので、もう少しAdobe Creative SDKについて勉強してみようと思っています。また何かあれば、こちらのブログでも紹介していければと思っています。

今回の記事は、どちらかと言えばFile APIとDrag and Drop APIの話がメインとなりました。これらの機能を実装する上で参考にしたサイトを最後に紹介させていただきます。

入門 HTML5
  • 『入門 HTML5』
  • 著者: Mark Pilgrim, 矢倉眞隆(監訳 / 翻訳), 水原文 (翻訳)
  • 出版社: オライリージャパン
  • 単行本: 256ページ
  • 発売日: 2011年4月23日
  • ISBN: 4873114829

関連記事

コメント

  • 必須

コメント