プログラミング初心者のための「HTML・CSS・Javascript」で作るアニメーションアプリ開発入門
プログラミング初心者の方にとってハードルが高く感じてしまいがちな「アニメーションアプリ開発」ですが、「HTML・CSS・Javascript」を利用してアニメーションアプリを作ることができます。
「アニメーションアプリの作り方」も複数あり、シンプルなアプリケーションを作りながら、少しずつ「アニメーションの作り方」を学んでいきましょう。
アニメーション作成のバリエーション
WEBアプリケーションでアニメーションを表示する方法には、
- HTML要素をJavascriptで操作
- CSS3のアニメーション機能を利用
- HTML5のCANVAS要素を利用
などの方法がありますが、今回は「HTML5のCANVAS要素を利用」する方法でアニメーション処理を作っていきたいと思います。
CANVASの座標系
CANVASは、Javascriptのプログラムで図形を描画しますが、まず初めに「CANVAS要素」をHTML内に用意しておきます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="robots" content="noindex,nofollow"> <title>WEB SCREEN APP</title> <style> #screen { border: solid 1px #000000; } </style> </head> <body> <canvas id="screen"></canvas> </body> </html>
CANVASの座標系は下図のように、右側へ行くほど「X座標値」が増加し、下側へ行くほど「Y座標値」が増加します。
左上の黒い枠線はCANVAS要素を表しています。
CANVASに図形を描画する方法
CANVAS要素のDOMを取得後、CANVASの2Dコンテキストを取得します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="robots" content="noindex,nofollow"> <title>WEB SCREEN APP</title> <style> #screen { border: solid 1px #000000; } </style> </head> <body> <canvas id="screen"></canvas> <script> //canvas要素のDOM(Document Object Model)を取得 var canvas = document.getElementById('screen'); var context; //コンテキストの取得可否チェック if(canvas.getContext){ //canvas要素のコンテキストを取得 context = canvas.getContext('2d'); } </script> <div id="link"></div> </body> </html>
描画処理を行う場合は、取得したコンテキストに対して描画用の関数を実行します。
例えば「円・半円・四角」を描画する場合は次のようになります。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="robots" content="noindex,nofollow"> <title>WEB SCREEN APP</title> <style> #screen { border: solid 1px #000000; } </style> </head> <body> <canvas id="screen"></canvas> <script> //canvas要素のDOM(Document Object Model)を取得 var canvas = document.getElementById('screen'); var context; //コンテキストの取得可否チェック if(canvas.getContext){ //canvas要素のコンテキストを取得 context = canvas.getContext('2d'); //現在の描画パスをリセット context.beginPath(); //円の描画設定 context.arc(30, 30, 15, 0, Math.PI*2, true); //円(円弧)の描画設定 context.arc(70, 30, 15, 0, Math.PI, true); //円と円弧の描画 //context.fill(); //四角の描画 context.fillRect(100, 15, 30, 30); } </script> <div id="link"></div> </body> </html>
このプログラムを実行すると下図のように、塗りつぶされた「円・円弧・四角」が表示されます。
CANVASヘの描画方法にはさまざまな方法が用意されていますので、図形を描画しながらCANVASへの図形描画方法を身に付けていきましょう。
アニメーション機能を実装してみよう!
今回作成するアニメーションは下の動画のようなものです。
「MOVE」ボタンを押すと、ボールが動き出し、「STOP」ボタンを押すとボールが停止します。
動画内で「動いているボール」を管理するためにボールの「クラス(設計図)」を作ります。
「クラス」とは、「オブジェクトの設計図」のことを言いますが、今回の場合、「オブジェクト」は「動いているボール」に相当します。
「動いているボールの設計図」が「クラス」となります。
- ボールのX座標位置
- ボールのY座標位置
- ボールの移動速度
- ボールの回転角度
などの情報を「クラス」として定義し、ボールの「実体(インスタンス)」を作ります。
クラスをJavascriptで定義すると下記のようになります。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="robots" content="noindex,nofollow"> <title>WEB SCREEN APP</title> <style> body { margin: 0; } #screen { width: 50%; height: 50%; border: solid 1px #000000; } </style> </head> <body> <canvas id="screen"></canvas> <script> //canvas要素のDOM(Document Object Model)を取得 var canvas = document.getElementById('screen'); var context; class Ball{ constructor(x, y, width, height, rotFlg, speed,image_fname ) { this.posX = x; // X座標位置 this.posY = y; // Y座標位置 this.width = width; // 画像の横幅 this.height = height; // 画像の縦幅 this.posXmoveDirection = true; // true : X座標増加, false : X座標減少 this.posYmoveDirection = true; // true : Y座標増加, false : Y座標減少 this.moveSpeed = speed; // 移動速度 this.rotateAngle = 0; // 回転角度 this.rotateFlag = rotFlg; // 回転フラグ // Imageオブジェクト生成 var img = new Image(); // 画像ファイルを読み込み img.src = image_fname; // 画像ファイルをセット this.image = img; } move(){ if( this.posXmoveDirection === false ) { this.posX -= this.moveSpeed; } else { this.posX += this.moveSpeed; } if( this.posYmoveDirection === false ) { this.posY -= this.moveSpeed; } else { this.posY += this.moveSpeed; } } } //コンテキストの取得可否チェック if(canvas.getContext){ //canvas要素のコンテキストを取得 context = canvas.getContext('2d'); } </script> </body> </html>
「constructor(コンストラクタ)」は、ボールの実体(インスタンス)を作る際に自動的に呼ばれて実行されるメソッドです。
クラスは、「情報(プロパティ)」と「動作(メソッド)」を定義することができ、コンストラクタ内の「this.~」という部分では、プロパティを定義しています。
プロパティの部分は、
- posX
- ボールのX座標位置
- posY
- ボールのY座標位置
- width
- ボール画像の横幅
- height
- ボール画像の縦幅
- posXmoveDirection
- ボールのX座標の移動方向
- posYmoveDirection
- ボールのX座標の移動方向
- moveSpeed
- ボールの移動速度
- rotateAngle
- ボールの回転角度
- rotateFlag
- ボールの回転フラグ
- image
- ボールの画像イメージオブジェクト
となります。
一方、「動作(メソッド)」は、
- constructor
- コンストラクタ
- move
- ボールの移動メソッド
となっています。
コンストラクタには「引数」を渡すことができますので、ボールの実体(インスタンス)を作る際に「ボールの情報の設定値」を渡すことができます。
インスタンスを作る際には「new」キーワードの右にクラス名を書き、コンストラクタに渡す引数を指定します。
new クラス名(コンストラクタに渡す引数);
今回は、8個のボールを作りますので、8個のインスタンスを作ります。
ボールのインスタンスは配列に格納し、「ボールの移動」を行う際には、配列内のインスタンスに対して「move」メソッドを実行します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="robots" content="noindex,nofollow"> <title>WEB SCREEN APP</title> <style> body { margin: 0; } #screen { width: 50%; height: 50%; border: solid 1px #000000; } #control { background-color: #5f5f5f; padding: 2px } </style> </head> <body> <canvas id="screen"></canvas> <div id="control"> <button id="move_btn">MOVE</button> <button id="stop_btn">STOP</button> </div> <script> //canvas要素のDOM(Document Object Model)を取得 var canvas = document.getElementById('screen'); var context; var objArray = new Array(); class Ball{ constructor(x, y, width, height, rotFlg, speed,image_fname ) { this.posX = x; // CanvasのX座標 this.posY = y; // CanvasのY座標 this.width = width; // 画像の横幅 this.height = height; // 画像の縦幅 this.posXmoveDirection = true; // true : X座標増加, false : X座標減少 this.posYmoveDirection = true; // true : Y座標増加, false : Y座標減少 this.moveSpeed = speed; // 移動速度 this.rotateAngle = 0; // 回転角度 this.rotateFlag = rotFlg; // 回転フラグ // Imageオブジェクト生成 var img = new Image(); // 画像ファイルを読み込み img.src = image_fname; // 画像ファイルをセット this.image = img; } move(){ if( this.posXmoveDirection === false ) { this.posX -= this.moveSpeed; } else { this.posX += this.moveSpeed; } if( this.posYmoveDirection === false ) { this.posY -= this.moveSpeed; } else { this.posY += this.moveSpeed; } } } //コンテキストの取得可否チェック if(canvas.getContext){ //canvas要素のコンテキストを取得 context = canvas.getContext('2d'); // new Ball(X座標,Y座標,横幅,高さ,回転有無,移動速度,画像ファイル名) objArray.unshift( new Ball(150,75,30,30,false,0.4,"./img/image1.png"), new Ball(70,120,30,30,true,0.6,"./img/image2.png"), new Ball(45,115,30,30,true,0.8,"./img/image3.png"), new Ball(53,45,30,30,true,1.0,"./img/image4.png"), new Ball(178,103,30,30,false,1.2,"./img/image5.png"), new Ball(223,141,30,30,false,1.4,"./img/image6.png"), new Ball(255,111,30,30,false,1.6,"./img/image7.png"), new Ball(215,121,30,30,true,1.8,"./img/image8.png") ); } </script> </body> </html>
ボールを動かすためには「Ballクラス」の「moveメソッド」を実行します。
「moveメソッド」は、アニメーションの1フレームごとにボールのX・Y座標を移動しているだけなのですが、「moveSpeedプロパティ」の設定値でボールごとに、「ボールの移動量」を変化させています。
計算を簡単にするために移動角度は「45度」のみとしました。
こうすることで、「SIN・COS」などの三角関数で幾何計算を行わなくてもボールが移動できるようになります。
移動パターンは下図の4通りとなります。
ボールが「CANVAS要素の端」に到達した場合は、ボールの移動方向を反転させるため、
- posXmoveDirection
- ボールのX座標の移動方向
- posYmoveDirection
- ボールのX座標の移動方向
の2つの変数でボールの移動方向を制御しています。
「posXmoveDirection」が「true」の時にボールのX座標が増加し、「false」の時はX座標が減少します。
「posYmoveDirection」が「true」の時にボールのY座標が増加し、「false」の時はY座標が減少します。
ボールの移動方向を変更するタイミングは、CANVAS要素の「枠」にボールが触れた時です。
座標の増減を制御しているメソッドが、Ballクラスの「moveメソッド」です。
class Ball{ constructor(x, y, width, height, rotFlg, speed,image_fname ) { //省略 } move(){ if( this.posXmoveDirection === false ) { this.posX -= this.moveSpeed; } else { this.posX += this.moveSpeed; } if( this.posYmoveDirection === false ) { this.posY -= this.moveSpeed; } else { this.posY += this.moveSpeed; } } }
これでアニメーションを行う準備ができました。
アニメーションを行うためには、一定時間ごとに「ボールの座標」を移動する必要がありますが、この処理は「setInterval」「clearInterval」メソッドを使用します。
- setInterval
- 一定時間ごとに繰り返し処理を開始する
- clearInterval
- serIntervalで開始した繰り返し処理を停止する
「setInterval」と「clearInterval」は下記のようになります。
// 繰り返し処理を実行したい関数 function hoge(){ // 繰り返し実行したい処理を記述 } var timerID; // 繰り返し処理を開始 timerID = setInterval(hoge, 33); // 繰り返し処理を停止 clearInterval(timerID);
「setIntervalメソッド」の第1引数には「実行したいメソッド名」、第2引数には「繰り返し間隔」を「ミリ秒」で指定します。
戻り値には繰り返し処理を停止するためのIDが返ってきますので、「clearIntervalメソッド」の引数にこの値を渡すと、繰り返し処理を停止することができます。
これでようやくアニメーションができるようになりました。
完成したコードは下記のようになります。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="robots" content="noindex,nofollow"> <title>WEB SCREEN APP</title> <style> body { margin: 0; } #screen { width: 50%; height: 50%; border: solid 1px #000000; } #control { background-color: #5f5f5f; padding: 2px } </style> </head> <body> <canvas id="screen"></canvas> <div id="control"> <button id="move_btn">MOVE</button> <button id="stop_btn">STOP</button> </div> <script> var timer_id = null; var canvas = document.getElementById('screen'); var context; var timerID = null; var objArray = new Array(); const INTERVAL = 1000; class Ball{ constructor(x, y, width, height, rotFlg, speed,image_fname ) { this.posX = x; // CanvasのX座標 this.posY = y; // CanvasのY座標 this.width = width; // 画像の横幅 this.height = height; // 画像の縦幅 this.posXmoveDirection = true; // true : X座標増加, false : X座標減少 this.posYmoveDirection = true; // true : Y座標増加, false : Y座標減少 this.moveSpeed = speed; // 移動速度 this.rotateAngle = 0; // 回転角度 this.rotateFlag = rotFlg; // 回転フラグ // Imageオブジェクト生成 var img = new Image(); // 画像ファイルを読み込み img.src = image_fname; // 画像ファイルをセット this.image = img; } move(){ if( this.posXmoveDirection === false ) { this.posX -= this.moveSpeed; } else { this.posX += this.moveSpeed; } if( this.posYmoveDirection === false ) { this.posY -= this.moveSpeed; } else { this.posY += this.moveSpeed; } } } //コンテキストの取得可否チェック if(canvas.getContext){ //canvas要素のコンテキストを取得 context = canvas.getContext('2d'); // new Ball(X座標,Y座標,横幅,高さ,移動速度,回転有無,画像ファイル名) objArray.unshift( new Ball(150,75,30,30,false,0.4,"./img/image1.png"), new Ball(70,120,30,30,true,0.6,"./img/image2.png"), new Ball(45,115,30,30,true,0.8,"./img/image3.png"), new Ball(53,45,30,30,true,1.0,"./img/image4.png"), new Ball(178,103,30,30,false,1.2,"./img/image5.png"), new Ball(223,141,30,30,false,1.4,"./img/image6.png"), new Ball(255,111,30,30,false,1.6,"./img/image7.png"), new Ball(215,121,30,30,true,1.8,"./img/image8.png") ); } function move(){ context.clearRect(0, 0, canvas.width, canvas.height); objArray.forEach(function( obj ) { if(obj.posX >= canvas.width - (obj.width / 2)){ obj.posXmoveDirection = false; } if(obj.posX <= (obj.width / 2)){ obj.posXmoveDirection = true; } if(obj.posY >= canvas.height - (obj.height / 2)){ obj.posYmoveDirection = false; } if(obj.posY <= (obj.height / 2)){ obj.posYmoveDirection = true; } obj.move(); context.beginPath(); if(obj.rotateFlag === true){ context.save(); context.translate(obj.posX, obj.posY); obj.rotateAngle++; context.rotate(( obj.rotateAngle % 360 ) / 180 * Math.PI); context.translate(-obj.width/2, -obj.height/2); context.drawImage(obj.image, 0, 0); context.restore(); } else { context.drawImage(obj.image, obj.posX - (obj.width/2), obj.posY - (obj.height/2)); } }); } function start_move(){ timer_id = setInterval(move, 33); } function stop () { clearInterval(timer_id); } document.getElementById('move_btn').addEventListener('click', start_move, false); document.getElementById('stop_btn').addEventListener('click', stop, false); </script> </body> </html>
実際の動きは、下記リンクから確認することができます。
今回作成したアニメーションプログラムはシンプルなものでしたが、本格的なアニメーションを作成するためには「幾何計算」などの専門的な知識が必要となってきます。