プログラミング初心者のための「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に図形を描画する方法

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ヘの描画方法にはさまざまな方法が用意されていますので、図形を描画しながら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>

実際の動きは、下記リンクから確認することができます。

サンプル

今回作成したアニメーションプログラムはシンプルなものでしたが、本格的なアニメーションを作成するためには「幾何計算」などの専門的な知識が必要となってきます。

HOMEへ