1-8 キャンバスを使う2 画像の表示

JavaScriptでキャンバスに画像を表示するには、画像ファイルを読み込む必要があります。 本項では画像の読み込みとキャンバスへの表示の仕方を解説します。


(1)画像を読み込み表示する


example181.html ← 動作の確認
ソースコードは次のようになります。
01<!DOCTYPE html>
02<html lang="ja">
03<head>
04<meta charset="utf-8">
05<title>JavaScriptのテストプログラム</title>
06</head>
07<body>
08<canvas id="bg" width="800" height="720"></canvas>
09<script>
10var canvas = document.getElementById("bg");
11var cnt = canvas.getContext("2d");
12var img;
13window.onload = function() {
14 img = new Image();
15 img.src = "example181_illust.png";
16 img.onload = function() {
17  cnt.drawImage( img, 0, 0 );
18 }
19}
20</script>
21</body>
22</html>

画像の読み込みとキャンバスへの描画の手順は次のようになります。

1.画像を読み込む変数名(オブジェクト名)を宣言
 ↓
2.new Image()命令で Imageオブジェクトを作成
 ↓
3.image.src で 画像ファイルを指定、読み込みが開始される ※この命令で瞬時に読み込まれるわけではない
 ↓
4.drawImage命令で描画 ※読み込みが完了してから描画する


画像が読み込まれる前に描画しようとすると処理が停止してしまいますので、onload命令で画像が読み込まれたかを判断します。 image.onload は画像読み込みが完了した時に働く命令です。今回はこの命令内で画像を表示するようにしています。

image.onload = function() { 画像の読み込み完了時に行いたい処理 }


onload = function(){}の記述について
HTMLが読み込まれた時点で window.onload が働くことを 1-6 (1) で解説しました。 今回のソースコードでは window.onload = function() { 処理 } と記述しています。 これは window.onload で名称無しの関数 function() { } の処理を実行するという意味です。 JavaScriptではこのように名称の無い関数を記述できます。 image.onload = function() { } も同様に、画像が読み込まれた時点で名称無しの関数内に記述された処理が実行されます。


さて、画像を表示するだけでしたら次のようにimgタグ1行で済みます。
example181_2.html ← 動作の確認
01<!DOCTYPE html>
02<html lang="ja">
03<head>
04<meta charset="utf-8">
05<title>HTMLとの比較</title>
06</head>
07<body>
08<img src="example181_illust.png">
09</body>
10</html>

これに比べるとJavaScriptで画像を扱う手順はややこしいと感じられるかもしれませんが、 JavaScriptを用いればHTMLだけでは難しい、次のような画像の拡大縮小や切り出し表示ができます。
example181_3.html ← 動作の確認
ソースコードは次のようになります。
01<!DOCTYPE html>
02<html lang="ja">
03<head>
04<meta charset="utf-8">
05<title>JavaScriptのテストプログラム</title>
06</head>
07<body>
08<canvas id="bg" width="880" height="720"></canvas>
09<script>
10var canvas = document.getElementById("bg");
11var cnt = canvas.getContext("2d");
12cnt.strokeStyle = "#000";
13cnt.strokeRect( 0, 0, 880, 720 );
14var img;
15window.onload = function() {
16 img = new Image();
17 img.src = "example181_illust.png";
18 img.onload = function() {
19  cnt.drawImage( img, 5, 5, 200, 180 );// 1/4サイズ
20  cnt.drawImage( img, 5, 180, 400, 360 );// 1/2サイズ
21  cnt.drawImage( img, 94, 116, 200, 230, 450, 0, 200, 230 );//切り出し
22  cnt.drawImage( img, 94, 116, 200, 230, 450, 240, 400, 460 );//切り出して拡大
23 }
24}
25</script>
26</body>
27</html>

drawImage命令は次のように3つのパタンで画像を表示できます。
context.drawImage( image, x, y )キャンバスの(x,y)の位置に
画像を表示
context.drawImage( image, x, y, w, h )キャンバスの(x,y)の位置に
幅w、高さhの大きさで画像を表示
context.drawImage( image, sx, sy, sw, sh, dx, dy, dw, dh )切り出し+拡大縮小表示
下記のようになります



(2)画像を動かす

HTMLだけでは静的な画像表示しかできませんが、JavaScriptを用いることで動的な画像表示が可能となります。 背景がスクロールしキャラクターが歩くサンプルを見てみましょう。
example182.html ← 動作の確認
ソースコードは次のようになります。
01<!DOCTYPE html>
02<html lang="ja">
03<head>
04<meta charset="utf-8">
05<title>JavaScriptのテストプログラム</title>
06</head>
07<body style="text-align:center;">
08<canvas id="bg" width="800" height="320"></canvas>
09
10<script>
11var canvas = document.getElementById("bg");
12var cnt = canvas.getContext("2d");
13
14var tmr = 0;//時間を管理する変数
15
16//キャラクターをアニメーションさせる番号の配列
17var CHR_ANI = [ 0, 1, 0, 2, ];
18
19//画像ファイル用の配列
20var img = [];
21var imgPre = [];
22
23//画像ファイルを読み込む関数
24function loadImg( n ) {
25 imgPre[n] = false;//読み込みできたかのフラグ
26 img[n] = new Image();
27 img[n].src = "example182_" + n + ".png";
28 img[n].onload = function() { imgPre[n] = true; }
29}
30
31//画像を表示する関数
32function drawImg( n, x, y ) {
33 if( imgPre[n] == true ) cnt.drawImage( img[n], x, y );
34}
35
36function drawImgS( n, x, y, w, h ) {
37 if( imgPre[n] == true ) cnt.drawImage( img[n], x, y, w, h );
38}
39
40for( var i = 0; i < 5; i ++ ) loadImg(i);//5枚の画像を読み込む
41
42window.onload = drawScene();
43function drawScene() {
44 var a, i, ofs;
45 tmr ++;//タイマーをカウント
46
47 ofs = tmr%240;
48 for( i = 0; i <= 4; i ++ ) drawImg( 3, 240*i-ofs, 0 );//遠景の雲 画像サイズW240 x H120
49
50 ofs = (tmr*2)%80;
51 for( i = 0; i <= 10; i ++ ) drawImg( 4, 80*i-ofs, 80 );//山と地面 画像サイズW 80 x H240
52
53 a = tmr%4;
54 drawImgS( CHR_ANI[a], 350, 160, 100, 80 );//動物のアニメーション
55
56 setTimeout(drawScene,200);
57}
58</script>
59</body>
60</html>

JavaScriptで次の5つの画像を読み込んでいます。


複数の画像を効率良く扱うには配列を用います。 今回は画像ファイルの読み込みの他にキャラクターのアニメーションで配列を利用しています。 JavaScriptの配列は次のいずれかの記述で宣言します。

var 変数名 = []; ← 今回のソースコード20、21行
var 変数名 = [ 初期値0, 初期値1, 初期値2, ‥, ‥, ‥ ]; ←ソースコード17行
もしくは
var 変数名 = new Array();
var 変数名 = new Array(要素数);

※配列はプログラミングの重要な知識の1つとなりますので、以下の(3)で詳しく説明します。

今回は example182_0.png example182_1.png example182_2.png example182_3.png example182_4.png という連番の5枚の画像を使っています。 それらの画像を読み込むために用意した関数が loadImg() 、表示するために用意した関数が drawImg() と drawImgS() です。

まず画像を読み込む処理を見てみましょう。20、21行で2つの配列を用意しています。 img[n] に example182_n.png を読み込み、imgPre[n] はその画像が読み込まれたかどうかのフラグになっています。 プログラミングではフラグ用の変数に、ある条件が成り立つ時は1やtrue、成り立っていない時は0やfalseなどの値を入れ、 その値によって処理を分岐させることがよく行われます。
loadImgでn番の画像を読み込む時に imgPre[n] を false にし、読み込みが完了したら(onload命令で)この値が true になります。 drawImgとdrawImgSではn番の画像のフラグが true なら表示するようにしています。 drawImgはそのままのサイズで表示し、drawImgSは幅と高さを指定して表示します。

JavaScriptで複数の画像を読み込む際のポイントは枚数を増やし過ぎないことです。 理由は読み込み枚数が増えるほど読み込みに失敗する危険性が高まるためです。 複数の画像を扱う場合は、できるだけ1枚にまとめ切り出し表示するようにしましょう。

ソースコードとリソースは最終的にネット上に置かれる
ローカル(ご自身が開発に使われているPC)でテストしている段階では、多くの枚数を瞬時あるいは短時間で読み込むことができます。 しかしソースコードと画像や音声などのリソースファイルは最終的にネット上にアップしますので、 ネットからの読み込み処理にはタイムラグが発生し、時に通信自体が途切れることもあります。 今回の例でキャラクターのアニメーション3枚を別々の画像にしていますが、 キャラクターの種類やアニメパタンが多い場合は、それらの画像を1枚にまとめるべきです。


次にアニメーションについて解説します。 setTimeout命令によるリアルタイム処理で画面の動きを実現しています。 時間管理用の変数 tmr をカウントし、その値から背景の表示位置とキャラクターのアニメーション番号を計算しています。 遠景の雲は割った余りを求める演算子%を用い ofs = tmr%240; という計算で(ofsの値は 0~239 を繰り返す)1ドットずつ位置をずらして表示しています。 山と地面も同様に ofs = (tmr*2)%80; という計算で、こちらは雲の2倍の速さ(2ドットずつ)で表示位置をずらしています。 二次元の背景をいくつかのパーツに分け、奥行きがあるように見せてスクロールさせることを多重スクロールといい、2Dのゲームソフトでよく使われる表現手法です。
キャラクターのアニメーションは3枚の絵の表示順番を配列で定義し(17行)、こちらもtmrを用いて0→1→0→2→0→1→0→2‥‥と繰り返して表示することで動きをつけています。


(3)配列について

配列は番号の付いた複数の箱に例えられます。この箱に数や文字を出し入れするイメージです。

JavaScriptでは数値、文字列、そしてオブジェクトを配列で扱うことができます。

var a = [ 1, 3, 5, 7, 9, ]; と宣言すると次のような配列変数が用意されます。
配列a[0]a[1]a[2]a[3]a[4]
初期値13579

var item = [ "みかん", "りんご", "レモン", ]; では次のような配列変数となります。
配列item[0]item[1]item[2]
初期値みかんりんごレモン

配列変数の四則演算や比較などの処理は通常の変数と一緒です。
例えば上記の配列で a[0] = a[1] + a[2]; とすれば a[0] の値は 3+5 で 8 となります。
item[0] = item[2]; item[2] = "オレンジ"; とすれば item[0]はレモン、item[2]はオレンジとなります。

var a = []; や var b = new Array(); とすると初期値は入らずに、その名称の配列を使うと宣言したことになります。
var c = new Array(10);のようにArrayの引数を与えた場合、c[0]からc[9]までの10個の配列を使うと宣言したことになります。この場合も初期値は入りません。

JavaScriptでは配列でオブジェクトを扱うことができます。 (2)のソースコードでは var img = []; で配列を宣言し、img[n] = new Image(); で Imageオブジェクトとして使っています。

配列がいくつ確保されているかをその配列の要素数といいます。 var a = [ 1, 3, 5, 7, 9, ];の要素数は5であり、var c = new Array(10);では10となります。

ここでは一次元の配列について説明しましたが、二次元以上の配列を作ることもできます。 ゲーム制作ではよく二次元配列でマップデータを管理します。 二次元配列は 2-4 で解説します。

JavaScriptの配列はとても柔軟
C系やJavaで配列を使うには、要素数を与えて宣言するか、初期値を入れて宣言する必要があります(その場合は初期値がいくつあるかが要素数となる)。 C系やJavaで例えば a[0]からa[4]までの配列を宣言した場合、a[5]に何らかの処理を行うことはできません。 要素数を超えた配列を扱おうとするとエラーとなり、多くの場合ソフトウェアが停止します。 一方JavaScriptの場合はまず配列名だけ宣言し、後から要素数を増やすことができます。 またC系やJavaは配列宣言の際に型(数値を扱うのか文字列を扱うのかなど)を明確にし、処理の途中で型を変更することはできません(C系やJavaでは変数自体の型を変更できない)。 JavaScriptの場合は今回のImageオブジェクトのように配列宣言後にその型を決めることが多々あります。
C系やJavaを学んでからJavaScriptを学ばれる方はJavaScriptの配列は便利だと感じられ、JavaScriptを学んだ後にC系やJavaを学ばれる方は他の言語の配列は窮屈に感じられるかもしれません。 JavaScriptの配列の扱いは他のプログラミング言語より柔軟性があり、C系やJavaと違う点があることを知っておかれると良いでしょう。



前のページへ / 次のページへ

お気軽にお問い合わせ下さい →