canvasにローカルの画像ファイルを描画するには

適当なお絵描きサイトをHTML5, canvas, javascriptで作ってました。

その時にやりたかったのが、canvas内にローカルの画像ファイルを描画し、

その画像を背景にお絵描きすることだったんですが、意外につまづいたので、メモしておきます。


仕様の概要はこんな感じです

  • input type="file"を使ってローカルファイルを参照すること
  • 参照した画像ファイルをcanvasに描画すること
  • 画像ファイルをサーバにアップロードしないこと(ローカルで完結)


ソースは以下の通り

HTML

  <div id="drawArea">
    <canvas id="myCanvas" width="300" height="300"></canvas>
  </div>
  <div id="uploadArea">
    <input id="uploadFile" name="image" type="file" />
  </div>

javascript

  $("#uploadFile").change(function() {
    var canvas = $("#myCanvas");
    var ctx = canvas[0].getContext("2d");

    // 選択されたファイルを取得
    var file = this.files[0];

    // 画像ファイル以外は処理中止
    if (!file.type.match(/^image\/(png|jpeg|gif)$/)) return;

    var image = new Image();
    var reader = new FileReader();

    // File APIを使用し、ローカルファイルを読み込む
    reader.onload = function(evt) {

      // 画像がloadされた後に、canvasに描画する
      image.onload = function() {
        ctx.drawImage(image, 0, 0);
      }

      // 画像のURLをソースに設定
      image.src = evt.target.result;
    }

    // ファイルを読み込み、データをBase64でエンコードされたデータURLにして返す
    reader.readAsDataURL(file);
  });


つまづいたところは以下の部分ですね。

  // 画像がloadされた後に、canvasに描画する

  // ** 失敗 **
  // 最初はこう書いてた 
  // このタイミングだと、imageのloadが終了していないため、canvasに何も描画されなかった(´・ω・`)
  ctx.drawImage(image, 0, 0);

  // ** 成功 **
  // imageのloadが完了してから、drawImageすれはOK
  image.onload = function() {
    ctx.drawImage(image, 0, 0);
  }

div要素の中に画像を追加するといった場合は問題ありませんが、

$("div").append(image); // これでOK

canvasに画像を描画する時は、ロードが完了してからという処理を加えないとダメです。


ちなみに、本来javascriptはローカルファイルを参照できませんが、
HTML5のFile API(FileReader)のおかげでこれができるようになりました。