2-2. 画像の表示方法

JavaScriptではcanvas要素を検出し、そこに描画する

samplejpg

HTML5を使うとJavaScriptで画像の描画が行えるようになります。 しかし、JavaScriptで描画が行えるのはHTML文書内にcanvas要素として定義された領域に対してだけです。

このため、JavaScriptを使って画像の描画を行いたい場合には、まず、HTML文書にcanvas要素を定義する必要があります。 レイヤー的な処理を行いたい場合には、複数のcanvas要素を定義し、その表示位置をcssで指定しておきます。 JavaScriptでは、定義されたcanvas要素を検出し、それに対して描画メソッドを実行します。

HTML文書とCSSについては前節で説明しましたので、本節ではJavaScript側の処理について説明して行きます。

まず、canvas要素を検出する

canvas1 = document.getElementById('id_canvas1');
if (!canvas1 || !canvas1.getContext) {
  alert("本ページの閲覧はHTML5対応ブラウザで行ってください");
  return false;
}
ctx1 = canvas1.getContext('2d');
canvas2 = document.getElementById('id_canvas2');
ctx2 = canvas2.getContext('2d');

上記のコードでは前節に掲載したHTML文書のサンプル内にあるcanvas要素を検出します。 canvas要素は二つ定義されていますので、上記のコードでは二つのcanvas要素の両方を検出しています。

canvasへの描画は実際にはcanvas要素の2dコンテキストに対して行います。 このため、上記では、検出したcanvas要素からコンテキストを取得しています。

HTML5に対応していないブラウザにはcanvas要素のgetContextメソッドが存在しません。 このため、上記では、その事を利用してHTML5に未対応のブラウザでアクセスされた場合に警告メッセージを表示しています

取得したコンテキストを使って図形を描画

ctx1.fillStyle = "rgb(0,0,0)";
ctx1.fillRect(0,0,320,240);
ctx1.beginPath();
ctx1.strokeStyle = "rgb(255,255,255)";
ctx1.lineWidth = 4;
ctx1.rect(100,100,50,50);
ctx1.stroke();

上記のコードではcanvas領域全体を黒く塗りつぶした後、線幅を4ピクセルにして正方形を描画しています。 canvas領域は何も描画していない段階では透明です。 このため、上記のcanvas領域の上には別のcanvas領域が定義されていますが上記の描画結果は正しく表示されます。

canvas要素には他にも図形描画用のメソッドが色々と用意されていますが、基本的な使い方は上記と同様です。

サンプルページを見る

取得したコンテキストを使って画像ファイルを描画

var picture = new Image();
picture.onload = function() {ctx1.drawImage(picture,0,0);};
picture.src = "pic.png";

上記のコードではpic.pngファイルを読み込んでcanvasに描画しています。 注意しなければならないのは、canvasへの描画は画像の読み込みが完了した後に行わなければならない、という事です。

このため、上記のコードではImageオブジェクトのonloadイベント関数の中から描画を行っています。 画像の読み込みはsrc定義が行われたタイミングで開始されますので、onloadイベント用の関数定義はsrc定義よりも前に行っておくのが安全です。

サンプルページを見る

背景画像にテキスト情報をオーバーレイしてブリンク表示

onload = function() {
  // まず、canvas要素を検出する
  canvas1 = document.getElementById('id_canvas1');
  if (!canvas1 || !canvas1.getContext) {
    alert("本ページの閲覧はHTML5対応ブラウザで行ってください");
    return false;
  }
  ctx1 = canvas1.getContext('2d');
  canvas2 = document.getElementById('id_canvas2');
  ctx2 = canvas2.getContext('2d');
  // 画像ファイルを表示
  picture = new Image();
  picture.onload = function() {ctx1.drawImage(picture,0,0);};
  picture.src = "pic.png";
  // インターバルタイマーでOverlayテキストをブリンク表示
  stat = true;
  overlay(ctx2,stat);
  timerID = setInterval('timerfunc()',500);
}
function timerfunc() {
  overlay(ctx2,stat);
  if (stat) {
    stat = false;
  } else {
    stat = true;
  }
}
function overlay(ctx,drawtext) {
  var str = "この動物は日本では猫と呼ばれています";
  ctx.font = "bold 14px 'Times New Roman'";
  tm = ctx.measureText(str);
  var posx = (320-tm.width-20)/2;
  var posy = 10;
  ctx.fillStyle = "rgba(0,255,0,0.2)";
  ctx.clearRect(posx,posy,tm.width+20,14+40);
  ctx.fillRect(posx,posy,tm.width+20,14+40);
  ctx.fillStyle = "rgb(255,255,255)";
  ctx.beginPath();
  ctx.strokeStyle = "rgb(255,255,0)";
  ctx.rect(posx,posy,tm.width+20,14+40);
  ctx.stroke();
  if (drawtext) {
    ctx.fillText(str,posx+10,posy+14+20);
  }
}

上記のコードでは、z-indexが異なる2つのcanvasを使って画像データの上にテキスト情報をオーバーレイ表示しています。

canvasでは透明度を指定した描画が行えるため、単に画像の上にテキスト領域を半透明表示させたい場合には、使用するcanvasは一つで十分です。 しかし、同じ画像に対して複数回の半透明描画を行うと、透明度が低下していきます。 これは、半透明描画は既に描画されている画像に対して行われるためです。

これを回避するためには、clearRectメソッドによってその領域をクリアしてから描画すれば良いのですが、canvasが一つしか無い場合には、背景画像まで消去されてしまいます。

上記のコードでは、この問題を回避するために二つのcanvasを利用しています。 同様の処理は、各描画時に背景描画を行ってからテキスト描画を行うことでも実現できます。 しかし、このような方法では描画処理のオーバーヘッドが増加します。

サンプルページを見る

画像データを加工してから描画

onload = function() {
  // まず、canvas要素を検出する
  canvas1 = document.getElementById('id_canvas1');
  if (!canvas1 || !canvas1.getContext) {
    alert("本ページの閲覧はHTML5対応ブラウザで行ってください");
    return false;
  }
  ctx1 = canvas1.getContext('2d');
  canvas2 = document.getElementById('id_canvas2');
  ctx2 = canvas2.getContext('2d');
  // 画像ファイルの読み込み完了を待って加工表示
  picture = new Image();
  picture.onload = function() {modifydraw()};
  picture.src = "pic.png";
}

function modifydraw() {
  ctx2.fillStyle = "rgb(0,0,0)";
  ctx2.fillRect(0,0,320,240);
  ctx1.drawImage(picture,0,0);
  var imagedata = ctx1.getImageData(0,0,320,240);
  for (var y = 0; y < 240; y++) {
    for (var x = 0; x < 320; x++) {
      d1 = imagedata.data[(x+y*320)*4+0];
      d2 = imagedata.data[(x+y*320)*4+1];
      d3 = imagedata.data[(x+y*320)*4+2];
      mean = (d1+d2+d3)/3;
      imagedata.data[(x+y*320)*4+0] = mean;
      imagedata.data[(x+y*320)*4+1] = mean;
      imagedata.data[(x+y*320)*4+2] = mean;
    }
  }
  ctx2.putImageData(imagedata,0,0);
}

上記のコードでは、canvasのgetImageDataメソッドを使ってカラー画像のデータを取り込み、モノクロ化した後、putImageDataメソッドを使って描画しています。

この処理を実現するために、canvasは二つ使用しています。 カラー画像はcanvas1に描画し、モノクロ画像はcanvas2に描画しています。

このコードで特徴的なのは、canvas1へのカラー画像描画の前に、canvas2を黒く塗りつぶしている点です。 canvas2はz-indexによりcanvas1よりも上に表示されるように設定されているため、この処理によりcanvas1の表示内容は隠されます。

このように、二つのcanvasを使用すると、画面に表示したくないデータを描画しつつ、そのデータをgetImageDataで取得して加工用に利用する事ができます。

なお、2011年1月の時点では、Firefox/Chromeではローカル環境でgetImageDataメソッドは正しく動作しません。 これはセキュリティ上の配慮からの仕様のようですが、それぞれについて、以下のように設定すれば、ローカル環境でも動作させる事ができるようになります。

Firefox
1.URL入力フィールドに「about:config」と入力
2.警告メッセージを了解する
3:security.fileuri.strict_origin_policy の値を false に設定

Chrome
1.スタートメニューのアイコンのプロパティからchrome.exeの場所を特定
2.chrome.exeのショートカットを作成
3.リンク先の最後に「 --allow-file-access-from-files」を追加
4.以降は作成したショートカットから起動するようにする

サンプルページを見る

まとめ

HTML5で追加されたcanvas要素を使うとWebページ上に画像を描画できます。 従来通り、画像ファイルのデータをそのまま描画させることもできますが、それらを加工した上で表示させたり、ゼロから生成した画像を表示させることも出来ます。

canvas要素で提供されるメソッドは比較的単純なものばかりですが、JavaScriptでプログラミングすることで、様々な画像を描画できます。 HTML文書に複数のcanvasを定義して利用するようにすれば、canvasの応用範囲はさらに広がります。