| CANVASに写真を書き込む |
今回は次の様に写真(もしくは図)をCANVAS上に読み込んでみます。単なる写真の読み込みではなく、CANVASを重ね合わせるLAYER(層)について確認します。
実は下の図は、もともと1枚の写真ではなく、異なる2つのCANVASに描いた背景とロケットを重ね合わせたものです。

| 2つのCANVASに異なる図を書く |
早速、2つのCANVASを用意し、それぞれに背景とロケットを書いてみます。下の図の通り1つめの CANVAS には背景を、2つめの CANVAS にはロケットを書いてみます。

プログラムは次の通りです。(⇒デモプログラムへリンク)
行番42,43でそれぞれ CANVAS を設定しています。必ず一致させる必要はないと思いますが、ここではCANVASの大きさを背景画像の大きさに合わせています。次に行番8~20が ‘CANVAS_01’ への背景の描画、行番22~34が ‘CANVAS_02’ へのロケットの描画を行っています。ロケットの描画を例に説明しますと、行番27~28で画像ファイルを取り込み、行番31で読込完了確認し、行番32で画像をキャンバス領域に書き込んでいます。行番33はキャンバス領域を示す長方形枠を書いていますが、領域の確認用です。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>CANVAS レイヤー評価</title>
<script>
function draw_test() {
//2Dコンテキストオブジェクト生成
var cvs01 = document.getElementById('CANVAS_01');
var ctx01 = cvs01.getContext('2d');
//画像オブジェクト生成
var img01 = new Image();
img01.src = "background_01.jpg";
//canvasに画像を描画
img01.onload = function(){
ctx01.drawImage( img01 , 0 , 0 , 604 , 340 );
ctx01.strokeRect( 0 , 0 , 604 , 340 );
}
//2Dコンテキストオブジェクト生成
var cvs02 = document.getElementById('CANVAS_02');
var ctx02 = cvs02.getContext('2d');
//画像オブジェクト生成
var img02 = new Image();
img02.src = "space_rocket.png";
//canvasに画像を描画
img02.onload = function(){
ctx02.drawImage( img02 , 604/2-30 , 340/2 - 30 , 60 , 60 );
ctx02.strokeRect( 0 , 0 , 604 , 340 );
}
}
</script>
</head>
<body onLoad="draw_test()">
<H2>CANVAS レイヤー評価</H2>
<canvas id="CANVAS_01" width="604" height="340"></canvas>
<canvas id="CANVAS_02" width="604" height="340"></canvas>
</body>
</html>
| 2つのCANVASを重ね合わせる |
いよいよ2つのCANVASを重ね合わせます。デモプログラムへのリンクを開くと一番 最初の図の様に重ね合わさった画像が出来たと思います。(⇒デモプログラムへリンク)
重ね合わせのポイントは行番48~51の様に “CANVAS” を ”<div class=’cvs-layer’> ~ </div>” で囲み、行番7~10の “ <style> ~ </style> ” で “position” を定義しています。行番8で<div class=’cvs-layer’> ~ </div>” ブロック全体を相対位置に指定し、ブロック内の2つの “CANVAS” 左上角部を絶対位置(0,0)に設定することで、“CANVAS” を重ね合わせています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>CANVAS レイヤー評価</title>
<style>
.cvs-layer { position: relative; }
.cvs-layer canvas { position: absolute; top: 0; left: 0; }
</style>
<script>
function draw_test() {
//2Dコンテキストオブジェクト生成
var cvs01 = document.getElementById('CANVAS_01');
var ctx01 = cvs01.getContext('2d');
//画像オブジェクト生成
var img01 = new Image();
img01.src = "background_01.jpg";
//canvasに画像を描画
img01.onload = function(){
ctx01.drawImage( img01 , 0 , 0 , 604 , 340 );
ctx01.strokeRect( 0 , 0 , 604 , 340 );
}
//2Dコンテキストオブジェクト生成
var cvs02 = document.getElementById('CANVAS_02');
var ctx02 = cvs02.getContext('2d');
//画像オブジェクト生成
var img02 = new Image();
img02.src = "space_rocket.png";
//canvasに画像を描画
img02.onload = function(){
ctx02.drawImage( img02 , 604/2-30 , 340/2 - 30 , 60 , 60 );
ctx02.strokeRect( 0 , 0 , 604 , 340 );
}
}
</script>
</head>
<body onLoad="draw_test()">
<H2>CANVAS レイヤー評価</H2>
<div class="cvs-layer">
<canvas id="CANVAS_01" width="604" height="340"></canvas>
<canvas id="CANVAS_02" width="604" height="340"></canvas>
</div>
</body>
</html>
| ロケットを飛ばす |
それでは、折角なのでロケットを飛ばしてみます。(⇒デモプログラムへリンク) デモプログラムではロケットが、左から右へ高度を変えて移動します。アニメーションについては、“HTML5 CANVASを使う②” で行っていますので省略します。今回はロケットの角度等を変えるために使っている 移動(translate)、回転(rotate)等を中心に概略説明します。ロケット画像の位置,角度指定し表示する箇所は行番71~75です。考え方としてはCANVAS 自体を移動・回転させ、移動後のCANVASに描画する感じです。
座標を行番72で(pos_x , pos_y)に移動(translate)、行番73で( 45 + r_ang )度 回転(rotate)した後、行番74で(pos_x , pos_y)が、ロケット画像中心になる様に画像の幅と高さのそれぞれ 1/2 分マイナス方向に移動し描画しています。
行番71 “save” はCANVAS 描画情報を保存し、行番75 “restore” は描画情報を復元する機能があります。 保存の対象は、線形状、色・不透明度、シャドウ、文字関係、変形情報などですが、ここでの目的は座標変換前に変形情報(setTransform,rotate,translate)を保存し、処理後に復元し元に戻しています。
ロケットの進行方向を決めるロジックについて簡単に説明します。今回はロケットの進行状態を4つに区分し、変数 “ph_no” に0~3の数値を設定します。行番78で先回の進行状態の変更からの処理回数を計測し、一定回数を超えたら変数 “ph_no” を 0,1,2,3 の順で変化させます。行番86~92では、進行状態が切り換わったことを判断し、状態が “0” か “2” の時には水平移動(r_ang = 0)、 “1” か “3” の時には乱数を発生させ値によって、上昇(r_ang = 45)/下降(r_ang = -45)を確定します。つまり、[水平移動]→[上昇/下降]を繰り返し実行することになります。
ただし、このままではロケットが上下方向の境界を越えてしまうこともありますので、行番95~96で境界を超える前に方向を逆転させる処理を入れています。行番99~100は、確定した上昇/下降の内容に応じて、上下方向位置(pos_y)を増減させます。
行番103~104は水平方向位置(pos_x)を変更しています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>CANVAS レイヤー評価</title>
<style>
.cvs-layer { position: relative; }
.cvs-layer canvas { position: absolute; top: 0; left: 0; }
</style>
<script>
const CH_RAD = Math.PI/180;
function draw_test() {
var img01_w = 0 ; var img01_h = 0 ;
const t_img_w = 60 ;
const t_img_h = 60 ;
var r_ang = 0 ; //ロケットの角度設定(45/0/-45)
var pos_x = 0 ; //ロケットの中心座標(X)
var pos_y = 0 ; //ロケットの中心座標(Y)
var lp_cn = 0 ; //処理ループ繰り返し回数
var ph_no = 9 ; //処理フェーズ管理変数
var fl_md = 0 ; //フェーズ切り替え検出用変数
//2Dコンテキストオブジェクト生成
var cvs01 = document.getElementById('CANVAS_01');
var ctx01 = cvs01.getContext('2d');
//2Dコンテキストオブジェクト生成
var cvs02 = document.getElementById('CANVAS_02');
var ctx02 = cvs02.getContext('2d');
//画像オブジェクト生成
var img02 = new Image();
img02.src = "space_rocket.png";
//画像オブジェクト生成
var img01 = new Image();
img01.src = "background_01.jpg";
//canvas画像描画(背景)
img01.onload = function(){
img01_w = img01.width ;img01_h = img01.height ;
//背景画像サイズに合わせ、キャンパスサイズ設定
cvs01.width = img01_w ; cvs01.height = img01_h ;
cvs02.width = img01_w ; cvs02.height = img01_h ;
//背景画像描画
ctx01.drawImage( img01 , 0 , 0 , img01_w , img01_h );
//開始時 Y 位置
pos_y = img01_h / 2 ;
cvs02_draw_00 () ;
}
//メイン画像描画
function cvs02_draw_00 () {
ctx02.clearRect( 0 , 0 , img01_w , img01_h );
//【参考】傾きを変えずに配置する場合
//ctx02.drawImage( img02 , (img01_w - t_img_w) / 2 , (img01_h - t_img_h) / 2 , t_img_w , t_img_h );
//座標・傾きを変える
ctx02.save();
ctx02.translate( pos_x , pos_y );
ctx02.rotate( (45 + r_ang) * CH_RAD );
ctx02.drawImage( img02 , - t_img_w / 2 , - t_img_h / 2 , t_img_w , t_img_h );
ctx02.restore();
//一定回数繰り返したら方向を変える
lp_cn = lp_cn + 1 ;
if(lp_cn > 70 + 70 * Math.random() ){
lp_cn = 0 ; fl_md = 0 ;
if( ph_no > 3 ){ ph_no = 0 ;
} else { ph_no = ph_no + 1 ; }
}
//次の進む方向を決める
if(fl_md == 0){
fl_md = 1 ;
if(ph_no == 1 || ph_no == 3){
if (Math.random() > 0.5) { r_ang = -45 ;
} else { r_ang = 45 ; }
}else{ r_ang=0 ; }
}
//上下端近くで強制的に向きを変える
if(pos_y >= (img01_h - 0.7 * t_img_h)){ r_ang = -45 ; }
if(0.7 * t_img_h >= pos_y){ r_ang = 45 ; }
//水平移動以外(角度≠0)の時、pos_yを増減する
if( r_ang > 0 ) { pos_y = pos_y + 2 ; }
if( 0 > r_ang ) { pos_y = pos_y - 2 ; }
//横方向移動の、pos_x 増加
pos_x = pos_x + 2 ;
if( pos_x > img01_w + t_img_w ){ pos_x = -1 * t_img_w ; }
requestAnimationFrame(cvs02_draw_00) ;
}
}
</script>
</head>
<body onLoad="draw_test()">
<H2>CANVAS レイヤー評価</H2>
<div class="cvs-layer">
<canvas id="CANVAS_01"></canvas>
<canvas id="CANVAS_02"></canvas>
</div>
</body>
</html>
| 座標変換について |
座標変換という表現は正しくないかも知れませんが、今回ロケットの移動に使用した “transform” , “rotate” について変換の内容をメモしておきたいと思います。
下の図のロケットの周辺は、本来無色透明ですが、範囲が判りやすい様に背景色をつけています。CANVASの座標は、原点に対し横軸は右向、縦軸は下向がそれぞれプラス方向になっています。図はロケットをCANVAS領域中心位置に水平右向きになる様に移動させる場合について書いています。この場合、まず “translate” で移動量を指定し座標軸を①から②の位置に移動します。ロケットはもともと①の図の通り傾いていますので、次に “rotate” で座標軸を45度回転させます。(図②→③) 最後に③の座標軸上で “drawImage” を使って、ロケット自身の高さと幅の1/2分だけ、それぞれマイナス方向にずらして描画すると図④の様になります。

| まとめ |
今回のロケットデモは必ずしも CANVAS を重ね合わせなくても良いのかもしれませんが、とりあえずCANVASによるレイヤーの考え方と座標変換の考え方を勉強することが出来ました。
自分としてはデモの内容もそれなりに気に入ってます。
(⇒デモプログラムへリンク)
別の検討をしていて、“iframe”タグを使って他のページを組み込めることを知りましたので、下記の通り組み込んでみました。幅をぴったり合わせられると良いのですが、また確認出来たら修正します。
次回はCANVASシリーズはお休みして、マウス等のイベント処理について勉強しようと思っています。(あくまで予定です)