HTML5 Canvasを使う④

 

JavaScriptでイベント処理

CANVASなどの対象となる要素上でクリックなどを行った時のイベント処理方法について確認してみます。今回は、JavaScriptの「addEventListener()」を使ってイベント処理を行います。「addEventListener()」は、クリックなどのマウス操作、タッチパネルへのタッチ操作、キーボード操作などのイベントを監視し、イベントが発生時に対応する処理を実行するものです。下記の様に記載します。

対象要素.addEventListener( ①種類 , ②関数 , ③false )

①は、“click” などイベント種類、②は実行する関数、③はウィンドウからイベントターゲットの親までのイベント取得フェーズ(“true”/キャプチャフェーズ 、“false”/バブリングフェーズ)を指定します。

“click”イベントで座標取得

先回投稿でCANVAS上を動くロケットのアニメーションを作成しましたが、そのプログラムを流用し、CANVASをクリックし座標を取得する様にしてみます。イベントプロパティを使って下の図の通り座標が取得できる様ですので、実際に確認してみました。

確認に使ったプログラムは下記です。ロケットのアニメーションプログラムに行番11,行番57,行番116~129,行番142~148を追加しています。行番142~148は取得した座標を表示するTABLEです。行番11ではこのTABLEの枠線等の設定を行っています。行番57はイベントを監視する「addEventListener()」を設定しています。CANVAS(cvs02)上の “click” イベントを監視し、 イベントが発生した時には、行番116~129の関数 “cvs02_click” を実行します。この関数はクリック位置の座標を取得しテーブル(表)にデータを設定しています。
座標を取得するだけではあまりおもしろくないので、行番127,128で“offset X / Y”の値をそれぞれロケットの中心座標(pos_x,pos_y)に代入しています。この処理によってロケットは即座にクリック位置に移動する様になります。(→デモへのリンク

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>CANVAS レイヤー評価</title>

<style>
  .cvs-layer { position: relative; height:370px; }
  .cvs-layer canvas { position: absolute; top: 0; left: 0; }
  table, td, th { border: 1px #808080 solid; border-collapse : collapse;}
</style>

<script>

const CH_RAD = Math.PI/180;
var pos_x = 0 ;
var pos_y = 0 ;

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 ;
  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 ;

    cvs02.addEventListener("click",cvs02_click,false);

    //背景画像描画
    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) ;
  }

}


function cvs02_click(event){
	document.getElementById('scr-x').innerHTML=Math.floor(100*event.screenX)/100 ;
	document.getElementById('scr-y').innerHTML=Math.floor(100*event.screenY)/100 ;
	document.getElementById('pag-x').innerHTML=Math.floor(100*event.pageX)/100 ;
	document.getElementById('pag-y').innerHTML=Math.floor(100*event.pageY)/100 ;
	document.getElementById('clt-x').innerHTML=Math.floor(100*event.clientX)/100 ;
	document.getElementById('clt-y').innerHTML=Math.floor(100*event.clientY)/100 ;
	document.getElementById('oft-x').innerHTML=Math.floor(100*event.offsetX)/100 ;
	document.getElementById('oft-y').innerHTML=Math.floor(100*event.offsetY)/100 ;
	document.getElementById('rct-x').innerHTML=Math.floor(100*event.target.getBoundingClientRect().left)/100 ;
	document.getElementById('rct-y').innerHTML=Math.floor(100*event.target.getBoundingClientRect().top)/100 ;
	pos_x = Math.floor(event.offsetX) ;
	pos_y = Math.floor(event.offsetY) ;
}

</script>
</head>


<body onLoad="draw_test()">
  <H2>CANVAS レイヤー評価</H2>
  <div class="cvs-layer">
  <canvas id="CANVAS_01"></canvas>
  <canvas id="CANVAS_02"></canvas>
  </div>

  <TABLE>
    <TR><TD>screen X / Y</TD><TD id="scr-x">―</TD><TD id="scr-y">―</TD></TR>
    <TR><TD>page X / Y</TD><TD id="pag-x">―</TD><TD id="pag-y">―</TD></TR>
    <TR><TD>client X / Y</TD><TD id="clt-x">―</TD><TD id="clt-y">―</TD></TR>
    <TR><TD>offset X / Y</TD><TD id="oft-x">―</TD><TD id="oft-y">―</TD></TR>
    <TR><TD>clientRect X / Y</TD><TD id="rct-x">―</TD><TD id="rct-y">―</TD></TR>
  </TABLE>

</body>
</html>

下図はプログラム実行状況です。CANVAS領域下にテーブル(表)が出来て、クリック位置座標が挿入されるのが確認出来ます。
(→デモへのリンク

その他のイベント

いくつか他のイベントを下の表に書いておきます。

イベント名 内 容
touchstart タッチが開始された瞬間
touchmove タッチされた後、指を画面上で動かす都度
touchend タッチされた後、指を画面から話した時
touchcancel タッチパネルに触れている最中にシステム側から強制的にキャンセルされた時
click マウスボタンをクリックした時
dblclick マウスボタンをダブルクリックした時
keydown キーボードのキーを押した時
keyup キーボードのキーを離した時
keypress キーボードのキーを押している時
load HTML文書の読み込みが完了した時
(画像など全リソース含む)
unload ページを移動する時
DOMContentLoaded HTML文書の読み込みが完了した時
(画像などのリソース含まない)
mouseover マウスカーソルが乗った時
mouseout マウスカーソルが外れた時
mousedown マウスボタンを押している時
mouseup マウスボタンを離した時
mousemove マウスカーソルが移動した時
change フォーム部品の状態が変更された時
submmit フォームのsubmitボタンを押した時
scroll 画面がスクロールした時

【参考】イベント関連情報 : Event reference

まとめ

「addEventListener()」を使ったイベント処理と座標取得について少し考え方の整理が出来ました。いろいろなシーンで応用できる様に少しづつ使って行きたいと思います。