CANVAS上でアニメーション |
今回は、CANVAS 上で動く簡単なアニメーションプログラムを作ります。円が左側から右方向に転がるものです。
(1)準備プログラム ←リンク有
準備として止まった状態の円(下図)を描画します。今回は静止画ということで、先回投稿したCANVASへの描画の復習となりますが、作成の過程でJavaScriptのオブジェクト変数(?)の使い方、JavaScriptからCANVAS要素の生成方法を確認できたのは良かったと思っています。
それでは、プログラムの確認です。先ほども書いた通り、今回はJavaScriptから CANVAS要素を生成するので、HTML内でCANVAS領域を設定していません。JavaScript内の行番13でCANVAS要素を生成し、行番14,15で領域設定、行番17で2Dコンテキスト取得、行番18でBODYタグ内にCANVASを設定しています。
行番24~26で円弧描画情報をオブジェクト配列(?)に定義し、行番29~32では背景(赤)とベース部(黒)を描画します。
次に円の部分を描画します。行番24~26では、3色分の円弧をそれぞれ定義しています。この3つの円弧の情報等を行番35~43のループ内で1つづ処理し、パス形成後内部を塗り潰し完成です。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>描画テスト</title> <script type="text/javascript"> <!-- function draw_test() { const cvs_w=500; const cvs_h=240; const canvas=document.createElement('canvas'); //canvas要素生成 canvas.width=cvs_w; //canvas幅設定 canvas.height=cvs_h; //canvas高さ設定 const context=canvas.getContext('2d'); //2Dコンテキスト取得 document.body.appendChild(canvas); //BODYタグ var r_p=[]; //オブジェクト配列宣言 //オブジェクト配列に描画する円のパラメータ設定 //円中心座標(x,y),半径(r),始点角度(s),終点角度(t),回転方向(d),色(c) r_p.push({x:120,y:120,r:80,s:180,t:200,d:true,c:"#FFFFFF"}); r_p.push({x:120,y:120,r:80,s:150,t:270,d:false,c:"#00FF00"}); r_p.push({x:120,y:120,r:80,s:30,t:150,d:false,c:"#0000FF"}); context.fillStyle="#FF0000"; //塗り潰し色設定(赤) context.fillRect(0,0,cvs_w,cvs_h); //canvas全体を塗り潰す context.fillStyle="#000000"; //塗り潰し色設定(黒) context.fillRect(0,200,cvs_w,40); //canvasベース部を塗り潰す //配列変数データから、円を描画 for( let cn in r_p ){ context.beginPath() ; //パス定義開始 context.fillStyle=r_p[cn].c ; //塗り潰し色設定 //下の行は円弧定義 context.arc( r_p[cn].x , r_p[cn].y , r_p[cn].r , r_p[cn].s *Math.PI/180 , r_p[cn].t *Math.PI/180 , r_p[cn].d ); context.lineTo( r_p[cn].x , r_p[cn].y ); //扇形にする為、中心に線を引く context.closePath(); //パスを閉じる context.fill(); } } --> </script> </head> <body onLoad="draw_test()"> <h2>描画テスト</h2> </body> </html>
(2)アニメーションプログラム ←リンク有
いよいよ円の部分を動かします。円の部分を動かすのには一定時間ごとに特定の処理を繰り返す “setInterval” を使用します。使い方は下記の通り処理関数と処理間隔(ミリ秒)を設定します。
使い方 | setInterval ( 関数 , 処理間隔(ミリ秒) ) |
今回のプログラムでは、行番56で使用しています。“loop” という関数を50ミリ秒ごとに実行する様にしています。ということで描画部分(行番30~43)を含める様にして、行番29~54を “loop” 関数として記述します。関数の中に関数が入っていて少し違和感がありますが、いろいろ調べ実際に動かしてみた結果、この様な感じでいいみたいです。関数(draw_test)の中に関数(loop)を記載する利点は、“draw_test” で設定した変数等がそのまま利用できることと思っています。
いずれにしても “setInterval” によって、関数 “loop” が繰り返し実行される様になりました。CANVASによるアニメーションは基本、全て再描画する様です。従って行番30~33の背景もすべて最初から書き直します。行番36~43では円形状を描画します。
行番45は描画後、円形状の中心X座標を“3”加算しています。このの処理により、円形状は50ミリ秒毎に3ピクセルづつ右方向に移動します。行番46は、中心X座標が [描画領域+90ピクセル] を超えると、座標を [ -90ピクセル ] に戻しています。
同様に行番48~51では、円形状を回転させる為に円弧の始点と終点をそれぞれ加算しています。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>描画テスト(setInterval)</title> <script type="text/javascript"> <!-- function draw_test() { const cvs_w=500; const cvs_h=240; const canvas=document.createElement('canvas'); //canvas要素生成 canvas.width=cvs_w; //canvas幅設定 canvas.height=cvs_h; //canvas高さ設定 const context=canvas.getContext('2d'); //2Dコンテキスト取得 document.body.appendChild(canvas); //BODYタグ var r_p=[]; //オブジェクト配列宣言 //オブジェクト配列に描画する円のパラメータ設定 //円中心座標(x,y),半径(r),始点角度(s),終点角度(t),回転方向(d),色(c) r_p.push({x:-90,y:120,r:80,s:180,t:200,d:true,c:"#FFFFFF"}); r_p.push({x:-90,y:120,r:80,s:150,t:270,d:false,c:"#00FF00"}); r_p.push({x:-90,y:120,r:80,s:30,t:150,d:false,c:"#0000FF"}); //繰り返しループ(アニメーションメイン) function loop(){ //◆関数 loop 始 context.fillStyle="#FF0000"; //塗り潰し色設定(赤) context.fillRect(0,0,cvs_w,cvs_h); //canvas全体を塗り潰す context.fillStyle="#000000"; //塗り潰し色設定(黒) context.fillRect(0,201,cvs_w,40); //canvasベース部を塗り潰す //配列変数データから、円を描画 for( let cn in r_p ){ context.beginPath() ; //パス定義開始 context.fillStyle=r_p[cn].c ; //塗り潰し色設定 //下の行は円弧定義 context.arc( r_p[cn].x , r_p[cn].y , r_p[cn].r , r_p[cn].s *Math.PI/180 , r_p[cn].t *Math.PI/180 , r_p[cn].d ); context.lineTo( r_p[cn].x , r_p[cn].y );//扇形にする為、中心に線を引く context.closePath(); //パスを閉じる context.fill(); r_p[cn].x = r_p[cn].x + 3 ; //◆円中心x座標(x)加算 if(r_p[cn].x>cvs_w+90){r_p[cn].x=-90;} //◆円中心x座標(x)初期化 r_p[cn].s = r_p[cn].s + 3 ; //◆始点角度(s)加算 r_p[cn].t = r_p[cn].t + 3 ; //◆終点角度(t)加算 if(r_p[cn].s > 360){ r_p[cn].s = 0 ; } //◆始点角度(s)初期化 if(r_p[cn].t > 360){ r_p[cn].t = 0 ; } //◆始点角度(s)初期化 } } //◆関数 loop 終 setInterval( loop , 50 ); //◆一定時間毎に指定処理を繰り返す } --> </script> </head> <body onLoad="draw_test()"> <h2>描画テスト(setInterval)</h2> </body> </html>
折角なので、“setInterval” の使い方を補足します。今回は関数内に関数を書くことで、引数など意識していませんが、次の様に書いて、引数1,引数2,… を繰り返し実行する関数に渡すことが出来る様です。
tm_id = setInterval ( 関数 , 処理間隔(ミリ秒) , 引数1 , 引数2 , … ) |
また、“tm_id ”にハンドルIDが設定され、このハンドルIDを指定して、clearInterval(tm_id) ; で繰り返し処理を中止することも出来るとのことです。
(3)その他の方法について
“setInterval” の他に “setTimeout” , ”requestAnimationFrame” という方法でも同じ様なアニメーションを実現出来ます。 “setInterval” が1回の呼び出しで、繰り返し処理するのに対し、 “setTimeout” , ”requestAnimationFrame” は、1回しか処理しないので、毎回関数を呼び出す必要があります。下記は、サンプルプログラムへのリンクです。”setTimeout” と比較して、 ”requestAnimationFrame” は、表示ブラウザの画面更新に合わせて関数を実行する為、処理負荷が小さくスムーズな動きになる様です。一方、実行間隔を指定できないので、場合によっては実行タイミングを管理する処理を追加する必要があります。
“setInterval” のプログラムに対する変更箇所を説明します。殆どすべて同じです。下記プログラムの行番59の位置に “setInterval” がありましたが、”loop”関数を1度呼び出す様に変更しています。以後、”loop”関数内の行番55( ”requestAnimationFrame”)で自身関数(”loop”)を呼び出すことで繰り返し処理が実行されます。
“setTimeout” の場合も同じ考え方です。行番54はコメント化されていますが、“//” を消し、行番55を全消去すれば ”setTimeout”によってアニメーションが実行されます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>描画テスト(requestAnimationFrame)</title> <script type="text/javascript"> <!-- function draw_test() { const cvs_w=500; const cvs_h=240; const canvas=document.createElement('canvas'); //canvas要素生成 canvas.width=cvs_w; //canvas幅設定 canvas.height=cvs_h; //canvas高さ設定 const context=canvas.getContext('2d'); //2Dコンテキスト取得 document.body.appendChild(canvas); //BODYタグ var r_p=[]; //オブジェクト配列宣言 //オブジェクト配列に描画する円のパラメータ設定 //円中心座標(x,y),半径(r),始点角度(s),終点角度(t),回転方向(d),色(c) r_p.push({x:-90,y:120,r:80,s:180,t:200,d:true,c:"#FFFFFF"}); r_p.push({x:-90,y:120,r:80,s:150,t:270,d:false,c:"#00FF00"}); r_p.push({x:-90,y:120,r:80,s:30,t:150,d:false,c:"#0000FF"}); //繰り返しループ(アニメーションメイン) function loop(){ //◆関数 loop 始 context.fillStyle="#FF0000"; //塗り潰し色設定(赤) context.fillRect(0,0,cvs_w,cvs_h); //canvas全体を塗り潰す context.fillStyle="#000000"; //塗り潰し色設定(黒) context.fillRect(0,201,cvs_w,40); //canvasベース部を塗り潰す //配列変数データから、円を描画 for( let cn in r_p ){ context.beginPath() ; //パス定義開始 context.fillStyle=r_p[cn].c ; //塗り潰し色設定 //下の行は円弧定義 context.arc( r_p[cn].x , r_p[cn].y , r_p[cn].r , r_p[cn].s *Math.PI/180 , r_p[cn].t *Math.PI/180 , r_p[cn].d ); context.lineTo( r_p[cn].x , r_p[cn].y );//扇形にする為、中心に線を引く context.closePath(); //パスを閉じる context.fill(); r_p[cn].x = r_p[cn].x + 3 ; //◆円中心x座標(x)加算 if(r_p[cn].x>cvs_w+90){r_p[cn].x=-90;} //◆円中心x座標(x)初期化 r_p[cn].s = r_p[cn].s + 3 ; //◆始点角度(s)加算 r_p[cn].t = r_p[cn].t + 3 ; //◆終点角度(t)加算 if(r_p[cn].s > 360){ r_p[cn].s = 0 ; } //◆始点角度(s)初期化 if(r_p[cn].t > 360){ r_p[cn].t = 0 ; } //◆始点角度(s)初期化 } // setTimeout(loop, 50); //★一連loop処理後、再呼び出し window.requestAnimationFrame(loop); //★一連loop処理後、再呼び出し } loop(); //★1回関数を呼び出す } --> </script> </head> <body onLoad="draw_test()"> <h2>描画テスト(requestAnimationFrame)</h2> </body> </html>
まとめ |
いろいろ本やネットを調べながら作成していますが、JavaScriptといっても、外部ファイル、jQuery、その他のJavaScriptライブラリらしきものなどいろいろあります。また、書き方も違っていたりして、なかなか周囲に有識者のいない私にとっては難しいことが多いです。既におやじですし。
一方、フリーのライブラリー等でも、私からすると複雑な処理も比較的簡単に出来て驚かされることも多いです。
いずれにしても良い意味で脳の刺激になっているとは思いますので、辛抱強く継続していきたいと思います。
次回は引き続き、CANVASシリーズでドラッグアンドドロップか写真の描画あたりをTRYしてみようと思っています。
本当は、WordPressで作った本サイトから、直接アニメ動画を再生できるといいのですが、今のところそのスキルがないので諦めています。いずれもう少しスキルアップしたら、外観や機能性を考えたサイトの作り方も検討してみたいと思っています。
「HTML5 Canvasを使う②」への1件のフィードバック