JavaScript - ボールが跳ね返るアニメーションの作り方

ボールが動き続けるアニメーションを JavaScript で作ります。requestAnimationFrame() メソッドの使い方や、キャンバスに図形を描画してアニメーションを作る基本的な方法を学びましょう。

JavaScript - ボールが跳ね返るアニメーションの作り方
Photo by Ellen Qin / Unsplash

JavaScript でボールを動かそう

今回紹介するのは、JavaScript でボールが動かして、壁に当たったら跳ね返るアニメーションの作り方です。HTML の <canvas> 要素にボールを描画して、壁で跳ね返りながら動くようにプログラミングしていきますよ。

キャンバスの設定方法や JavaScript で図形を描く基本的な方法は知っていますか?詳しくは以下の記事で解説していますので、参考にしてください。

JavaScript - Canvas に図形を描く方法
HTML の canvas 要素を利用すると、JavaScript でグラフィックを描くことができます。基本的なメソッドを学んで、四角形や三角形、円やテキストなど簡単な図形を描画できるようになりましょう。

ボールが跳ね返るアニメーション

これから作っていくのは、下のようなアニメーションです。

ボールが跳ね返るサンプルアニメーション
ボールが跳ね返るサンプルアニメーション

記事の前半では、キャンバスにボールを描画して横方向に動かすコードを解説します。そして、記事の後半では、ボールが壁で跳ね返るようにコードを追加してアニメーションを完成させます。

描画を始めるための基本コード

では、JavaScript で描画を始めるための基本コードを書いていきましょう。

HTML でキャンバスを用意します。ここでは、描画エリアを幅 500、高さ 500 の正方形にします。

<!--キャンバスを用意する-->
<canvas id="canvasDemo" width="500" height="500">ボールが跳ね返るアニメーション</canvas>

CSS でキャンバスの周りに枠線を設定してください。この枠線はボールが当たったときに跳ね返る壁になりますよ。

/* キャンバスに灰色の枠線を設定する */
#canvasDemo {
  border: 5px solid lightgray;
}

JavaScript で描画するためのプロパティやメソッドを取得しておきましょう。

// 描画するためのプロパティやメソッドを取得する
const canvas = document.querySelector('#canvasDemo');
const ctx = canvas.getContext('2d');

JavaScript でキャンバスに描画する準備ができました。

変数を宣言する

続いて、ボールのアニメーションを作っていくために必要な変数を四つ用意しましょう。

一つ目は、ボールの大きさを表す変数 ballRadius です。変数を作らずに、大きさを表す数値をそのまま使うこともできますが、変数で参照できるようにしておけば、いつでも簡単にボールのサイズを変更することができますよ。

  • アニメーションの間はボールのサイズは変わらないので、const で宣言します。
  • ボールはこのあと arc() メソッドを利用して描いていきます。 arc() メソッドで指定する値は半径なので、変数に格納する値はボールの半径です。
// ボールの半径
const ballRadius = 100;

二つ目と三つ目は、ボールの位置を表す変数です。横の位置を表す変数を ballX、縦の位置を表す変数を ballY とし、ボールの中心座標を指定します。最初の位置は、キャンバスの中央にしましょう。

  • ボールの位置はアニメーションの間で変化するので、let で宣言します。
  • 変数 ballXcanvas.width / 2、変数 ballYcanvas.height / 2 で初期化します。これは、キャンバスの中央の位置を表します。
const ballRadius = 100;

// ボールの位置(最初はキャンバスの中央)
let ballX = canvas.width / 2;
let ballY = canvas.height / 2;

四つ目は、ボールの横移動を表す変数 speedX です。

  • この変数の値は、ボールが横移動し壁で跳ね返るときにプラスとマイナスが入れ替わるので、let で宣言します。
  • ここでは数値 5 を格納し、5ピクセルずつボールが横へ移動するようにします。
const ballRadius = 100;
let ballX = canvas.width / 2;
let ballY = canvas.height / 2;

// ボールの横移動
let speedX = 5;

以上で変数の用意ができました。これらの変数を使って、ボールを描いたり動かしたりしていきますよ。

ボールを描く関数を定義する

先ほど宣言した変数、ballXballYballRadius を利用してボール (円) を描きましょう。ボールを描画するコードは drawBall という関数でまとめます。円を描くための arc() メソッドについて詳しくはこちらをご覧ください。

const ballRadius = 100;
let ballX = canvas.width / 2;
let ballY = canvas.height / 2;
let speedX = 5;

// ボールを描く関数
function drawBall() {
  ctx.beginPath();
  ctx.arc(ballX, ballY, ballRadius, 0, Math.PI*2);
  ctx.fillStyle = '#0d7df5';
  ctx.fill();
}

ボールを描く関数を定義できたら、アニメーションのコードを書いていきましょう。

アニメーションを実行する - requestAnimationFrame()

アニメーションを実行するために使うのは、requestAnimationFrame() メソッドです。JavaScript でアニメーションを作るときに使われるメソッドには、他にも setInterval() がありますね。setInterval() は、関数を呼び出す間隔を自分で指定します。一方、requestAnimationFrame() は、ブラウザが適切なタイミングで画面を更新するので、より効率的でスムーズなアニメーションを作ることができますよ。

構文:

requestAnimationFrame(コールバック関数)

コールバック関数は、引数として別の関数に渡される関数のことです。requestAnimationFrame() では、アニメーション処理を行う関数を括弧 () の中に引数として指定します。

ここでは、アニメーション処理を行う関数名を animate とします。関数 animaterequestAnimationFrame() で呼び出すためには、以下のように書きます。コールバック関数内に requestAnimationFrame() メソッドを含めるところがポイントです。これによって、繰り返し処理が行われますよ。

// アニメーション処理を行う関数(コールバック関数)
function animate() {

  // アニメーション処理 
  // …

  // コールバック関数内でrequestAnimationFrame()を再度呼び出す
  requestAnimationFrame(animate);
}

// アニメーション開始
requestAnimationFrame(animate);

requestAnimationFrame() メソッドの使い方が分かったところで、次は、アニメーション処理を行う関数 animate を定義していきましょう。

ボールを右へ動かすには?

関数 animate では、位置を少しずつずらしならが繰り返しボールを描画する処理を行います。ボールを右へ動かすためには、ボールの x 座標を増やせばいいですね。ballX += speedX とすることで、次にボールを描画する際にボールの x 座標が2ピクセルずつ横へ移動しますよ。

function animate() {

  // ボールを描く
  drawBall();  
  // ボールの位置を横移動分動かす
  ballX += speedX;

  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

ここまでのコードを実行してみると、結果は以下のようになります。これは、繰り返し描画されたボールがずらりと横に並んでいる状態です。

ボールの移動を表すためには、ボールを描画する際に、その前に描いたボールを消しておく必要があります。「消す」と「位置をずらして描画する」を繰り返すことで、ボールの動きを表現できますよ。そのために使うのが、clearRect() メソッドです。

キャンバスをクリアする - clearRect()

clearRect() メソッドは、指定した範囲でキャンバスの内容を消去します。キャンバス全体をクリアしてからボールを描画するようにコードを追加しましょう。指定する範囲は、キャンバスの原点 (左上角) 0, 0 からキャンバスの幅 canvas.width と高さ canvas.height です。

function animate() {

  // キャンバス全体を消去する
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  drawBall();
  ballX += speedX;

  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

コードを実行してみましょう。「キャンバス全体を消去する」ことと「ボールの位置をずらしながらキャンバスに描画する」ことを繰り返すことで、ボールが右へ移動するアニメーションができました。

でも、このままだとボールが壁を通り越してしまいますね。次のセクションからは、ボールが壁で跳ね返るコードを追加していきますよ。

壁でボールを跳ね返すには?

ボールが壁に当たったら跳ね返るようにするために使うのは、if 文です。if 文の条件によって、横移動の向きを逆にして跳ね返るようにします。

function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  drawBall();
  ballX += speedX;

  // もしボールが右または左の壁に当たるなら
  if( ) {

    // 横移動の向きを逆にする処理
    // …

    }

  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

if 文について詳しくは以下の記事をご覧ください。

JavaScript - 条件分岐の基本 - if 文の書き方と true, false の意味
if 文は、「もし〇〇ならAをする、そうじゃなかったらBをする」のように、条件によって処理を分岐させるための文です。if 文を理解する際に必要になる、真理値(真偽値)の true と false についても、一緒に学んでいきましょう。

キャンバスの右側で跳ね返る

右へ動くボールがキャンバスの右端に当たるかどうかを調べましょう。if 文の条件に、「もしボールの右端がキャンバスの幅より大きいなら」という条件を指定します。

ボールの右端は、ボールの中心 ballX とボールの半径 ballRadius を足した値で表します。その値がキャンバスの幅 canvas.width より大きいなら、ボールが壁に当たっているということになりますね。

// もしボールの右端がキャンバスの幅より大きいなら
if(ballX + ballRadius > canvas.width) {

  }

跳ね返る動きを表すためには、左右の動きを逆にします。ボールの横移動を表す変数 speedX-speedX を代入すると、動く向きが逆になりますよ。

if(ballX + ballRadius > canvas.width) {

    // 横移動の向きを逆にする  
    speedX = -speedX;

  }

speedX がマイナスになると、ボールの x 座標が -2ピクセルずつ変化するので、ボールは左へ動きます。

キャンバスの左側で跳ね返る

続いて、左へ動くボールがキャンバスの左端に当たるかどうかを調べましょう。ボールの中心 ballX がボールの半径 ballRadius より小さいなら、ボールの左端が壁に当たっていることになります。「または」を表す演算子 || に続けて、条件を追加しますよ。

// もしボールの右端がキャンバスの幅より大きい
// または、ボールの中心座標がボールの半径より小さいなら
if(ballX + ballRadius > canvas.width || ballX < ballRadius) {

    speedX = -speedX;

  }

左へ動いているときの speedX はマイナスです。そこに -speedX を代入することで speedX はプラスになり、ボールは右へ動くようになります。

ボールが左右の壁で跳ね返るコード

ここまでで、ボールが左右の壁で跳ね返るアニメーションのコードができました。

// 描画するためのプロパティやメソッドを取得する
const canvas = document.querySelector('#canvasDemo');
const ctx = canvas.getContext('2d');
// ボールの半径
const ballRadius = 100;
//  ボールの位置(最初はキャンバスの中央)
let ballX = canvas.width / 2;
let ballY = canvas.height / 2;
//ボールの横移動
let speedX = 5;

// ボールを描く関数
function drawBall() {
  ctx.beginPath();
  ctx.arc(ballX, ballY, ballRadius, 0, Math.PI*2);
  ctx.fillStyle = '#0d7df5';
  ctx.fill();
}

// アニメーション処理を行う関数(コールバック関数)
function animate() {
  // キャンバス全体を消去する
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  // ボールを描く
  drawBall();
  // ボールの位置を横移動分動かす
  ballX += speedX;
  // ボールが右または左の壁に当たるなら
  if(ballX + ballRadius > canvas.width || ballX < ballRadius) {
    // 横移動の向きを逆にする  
    speedX = -speedX;
  }
  // コールバック関数内でrequestAnimationFrame()を再度呼び出す
  requestAnimationFrame(animate);
}
// アニメーション開始
requestAnimationFrame(animate);

ボールが上下左右の壁で跳ね返るコード

最後に、ボールを上下に動かすコードを追加してアニメーションを完成させましょう。

まずは、ボールの縦移動を表す変数 speedY です。ここでは数値 8 を格納し、8ピクセルずつボールが縦に動くようにします。

// ボールの縦移動
let speedY = 8;

次に、アニメーション処理を行う関数 animate にコードを追加していきます。ballY += speedY はボールを縦方向へ動かすための文です。これによって、ボールを描画する際に、ボールの y 座標が8ピクセルずつ縦に移動します。

// ボールの位置を縦移動分動かす
ballY += speedY;

さらに、if 文を追加して、ボールが下または上の壁に当たったら跳ね返るようにします。ボールの縦移動を表す変数 speedY-speedY を代入することで、動く向きが逆になりますよ。

// ボールが下または上の壁に当たるなら
if(ballY + ballRadius > canvas.height || ballY < ballRadius) {
  
  // 縦移動の向きを逆にする
  speedY = -speedY;

  }

以上で、上下左右4つの壁でボールが跳ね返るアニメーションのコードが完成です。

//**********************************
// ボールが上下左右の壁で跳ね返るコード
//**********************************

const canvas = document.querySelector('#canvasDemo');
const ctx = canvas.getContext('2d');
const ballRadius = 100;
let ballX = canvas.width / 2;
let ballY = canvas.height / 2;
let speedX = 5;
// ボールの縦移動
let speedY = 8;

function drawBall() {
  ctx.beginPath();
  ctx.arc(ballX, ballY, ballRadius, 0, Math.PI*2);
  ctx.fillStyle = '#0d7df5';
  ctx.fill();
}

function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  drawBall();
  ballX += speedX;
  // ボールの位置を縦移動分動かす
  ballY += speedY;

  if(ballX + ballRadius > canvas.width || ballX < ballRadius) {
    speedX = -speedX;
  }
  // ボールが下または上の壁に当たるなら
  if(ballY + ballRadius > canvas.height || ballY < ballRadius) {
    // 縦移動の向きを逆にする
    speedY = -speedY;
  }
  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

See the Pen JavaScript - Bouncing off the Walls by Pyxofy (@pyxofy) on CodePen.


まとめ

今回は、JavaScript の requestAnimationFrame() メソッドを使って、ボールが壁で跳ね返るアニメーションを作成しました。

HTML の <canvas> 要素を利用してアニメーションを作るためには、「キャンバスを消去する」ことと「キャンバスに描画する」ことを JavaScript で繰り返します。ボールの向きやスピードは、横移動 speedX と縦移動 speedY の値によって変わりますよ。いろいろな値を代入して動きの変化を試してみてくださいね。

最後まで読んでいただき、ありがとうございます。この記事をシェアしてくれると嬉しいです!

こちらもチェック!LinkedInThreadsMastodon X (Twitter) @pyxofyFacebook

関連記事

JavaScript - setInterval() を使ったアニメーションの基本
この記事では JavaScript でアニメーションを作る方法を解説します。ポイントとなるのは setInterval() メソッドと clearInterval() メソッドです。ボタンをクリックしてアニメーションを動かしたり止めたりする方法を学びましょう。
CSS Art – How to Make a Turntable
In this article you will learn how to use Hex colors and CSS pseudo-elements. Then we’ll make a simple turntable step-by-step.
CSS Animation – Horizontal and Vertical Movement
How can we move shapes from left to right, top to bottom on the internet? Let’s find out how in this step-by-step CSS animation article.
スクラッチプログラミング - ボールをバウンドさせよう
ボールをバウンドさせるときにポイントとなるブロックは、「もしはしについたら、はねかえる」です。ボールが画面(がめん)のはしにぶつかってはねかえるプログラムを、一緒(いっしょ)につくってみましょう。