canvas 簡単なアニメーション

簡単なアニメーション Java Script
スポンサーリンク

今回は簡単なアニメーションについて見ていきます。

ご紹介している内容はMDNのサイトのものになりますので、合わせてご参照ください。

MDN :基本的なアニメーション

スポンサーリンク

アニメーションを制御する

アニメーションを制御するには、以下のメソッドを使用します。

  • setInterval():指定した関数をミリ秒ごと遅れて実行します。
  • setTimeout:指定した関数をミリ秒後に実行します。
  • requestAnimationFrame():ブラウザにアニメーションを行うことを知らせます。コールバック関数を1つ引数として指定します。

時計

requestAnimationFrame()

function clock() {
  let time = new Date();
  const canvas37 = document.getElementById('sample37').getContext('2d');

  canvas37.save();
  canvas37.clearRect(0, 0, 500, 200);
  canvas37.translate(75, 75); //原点を指定します。
  canvas37.scale(0.4, 0.4); //スケーリング変換します
  canvas37.rotate(-Math.PI / 2); //12時を0度にするため回転させます
  canvas37.strokeStyle = 'black'; // 輪郭線を塗りつぶします
  canvas37.fillStyle = 'white'; // 図形の内側を塗りつぶします
  canvas37.lineWidth = 8; //線の幅を指定
  canvas37.lineCap = 'round'; //線の端のスタイルを指定

  // 文字盤の時間
  canvas37.save();
  for (let i = 0; i < 12; i++) {
    canvas37.beginPath();
    canvas37.rotate(Math.PI / 6); //30度ずつ回転(30度で1時間)
    canvas37.moveTo(100, 0); // 新しいパスの開始地点を指定
    canvas37.lineTo(120, 0); // 新しいパスの終了地点を指定
    canvas37.stroke(); //輪郭をなぞります
  }
  canvas37.restore();

  // 文字盤の分
  canvas37.save();
  canvas37.lineWidth = 5;
  for (i = 0; i < 60; i++) {
    //iは60以下、つまり分を表しています。
    if (i % 5 !== 0) {
      canvas37.beginPath();
      canvas37.moveTo(117, 0);
      canvas37.lineTo(120, 0);
      canvas37.stroke();
    }
    canvas37.rotate(Math.PI / 30); //6度ずつ回転(1分の角度は6度)
  }
  canvas37.restore();

  let sec = time.getSeconds();
  let min = time.getMinutes();
  let hr = time.getHours();
  hr = hr >= 12 ? hr - 12 : hr;
  //三項演算子
  // if(hr >= 12){hr = hr - 12}else{hr = hr}

  canvas37.fillStyle = 'black';

  // 時間の針
  canvas37.save();
  canvas37.rotate(
    hr * (Math.PI / 6) + (Math.PI / 360) * min + (Math.PI / 21600) * sec
  ); // hr* 30度 + 0.5度*分 + 0.008333333度*秒
  canvas37.lineWidth = 14;
  canvas37.beginPath();
  canvas37.moveTo(-20, 0);
  canvas37.lineTo(80, 0);
  canvas37.stroke();
  canvas37.restore();

  // 分針
  canvas37.save();
  canvas37.rotate((Math.PI / 30) * min + (Math.PI / 1800) * sec); // 6度*分数 + 0.1度*秒数
  canvas37.lineWidth = 10;
  canvas37.beginPath();
  canvas37.moveTo(-28, 0);
  canvas37.lineTo(112, 0);
  canvas37.stroke();
  canvas37.restore();

  // 秒針
  canvas37.save();
  canvas37.rotate((sec * Math.PI) / 30); // 6度*秒数
  canvas37.strokeStyle = '#D40000';
  canvas37.fillStyle = '#D40000';
  canvas37.lineWidth = 6;
  canvas37.beginPath();
  canvas37.moveTo(-30, 0);
  canvas37.lineTo(83, 0);
  canvas37.stroke();
  canvas37.beginPath();
  canvas37.arc(0, 0, 10, 0, Math.PI * 2, true); //針の先端
  canvas37.fill();
  canvas37.beginPath();
  canvas37.arc(95, 0, 10, 0, Math.PI * 2, true); //針の先端
  canvas37.stroke();
  canvas37.fillStyle = 'rgba(0, 0, 0, 0)';
  canvas37.arc(0, 0, 3, 0, Math.PI * 2, true); //秒針の中央の円
  canvas37.fill();
  canvas37.restore();

  canvas37.beginPath();
  canvas37.lineWidth = 14;
  canvas37.strokeStyle = '#325FA2';
  canvas37.arc(0, 0, 142, 0, Math.PI * 2, true);
  canvas37.stroke();

  canvas37.restore();

  window.requestAnimationFrame(clock);
}

window.requestAnimationFrame(clock);

y座標が常に0度になっていることに注意してください。

コードが長く難しく感じかもしれませんが、注意すべき点は「rotate」の設定のみになります。

「rotate()」が理解出来たらスッキリすると思います。

ループする画像

setInterval()

    <!-- JSのCanvasXSizeとCanvasYSIzeをおなじ幅と高さにする -->
    <canvas id="animation" width="800px" height="300px"></canvas>
const img = new Image();
img.src = 'https://hitoridemanabou.net/wp-content/uploads/2022/06/mountain.png';

// HTMLで指定した幅と高さを同じにする
const CanvasXSize = 800;
const CanvasYSize = 300;

let speed = 40; // サイズを変えるとスピードが変わる
let scale = 0.8; //数値を変えると画像が拡大縮小されます
let y = -4.5;

let dx = 0.75;
let imgW;
let imgH;
let x = 0;
let clearX;
let clearY;
let ctx;

img.onload = () => {
  imgW = img.width * scale; 
  imgH = img.height * scale; 

  if (imgW > CanvasXSize) {
    x = CanvasXSize - imgW; 
  }
  if (imgW > CanvasXSize) {
    clearX = imgW; 
  } else {
    clearX = CanvasXSize;
  }
  if (imgH > CanvasYSize) {
    clearY = imgH; 
  } else {
    clearY = CanvasYSize;
  }

  ctx = document.getElementById('animation').getContext('2d');

  return setInterval(draw, speed);
};

function draw() {
  ctx.clearRect(0, 0, clearX, clearY);

  if (imgW <= CanvasXSize) {
    if (x > CanvasXSize) {
      x = -imgW + x;
    }
    if (x > 0) {
      ctx.drawImage(img, -imgW + x, y, imgW, imgH);
    }
    if (x - imgW > 0) {
      ctx.drawImage(img, -imgW * 2 + x.y.imgW, imgH);
    }
  } else {
    if (x > CanvasXSize - imgW) {
      ctx.drawImage(img, x - imgW + 1, y, imgW, imgH);
    }
  }
  ctx.drawImage(img, x, y, imgW, imgH);
  x += dx;
}

MDNのサイトをベースに記述していますが、用意する画像サイズをcanvasタグに指定したサイズよりも大きいものにすれば、もう少しコードを短く記述することが出来ます。

const img = new Image();
img.src = 'https://hitoridemanabou.net/wp-content/uploads/2022/06/mountain.png';

// HTMLで指定した幅と高さを同じにする
const CanvasXSize = 800;
const CanvasYSize = 300;

let speed = 40; // サイズを変えるとスピードが変わる
let scale = 0.8; //数値を変えると画像が拡大縮小されます
let y = -4.5;

let dx = 0.75;
let imgW;
let imgH;
let x = 0;
let ctx;

img.onload = () => {
  imgW = img.width * scale; //1200*0.8 = 960
  imgH = img.height * scale; //800*0.8 = 640

  ctx = document.getElementById('animation01').getContext('2d');

  return setInterval(draw, speed);
};

function draw() {
  ctx.clearRect(0, 0, imgW, imgH);

  ctx.drawImage(img, x - imgW + 1, y, imgW, imgH);
  ctx.drawImage(img, x, y, imgW, imgH);
  x += dx;
}

動くボール

    <canvas id="animation02" width="600px" height="300px"></canvas>
const anima = document.getElementById('animation02');
const ctx = anima.getContext('2d');
let raf;

//ボールを描写
let ball = {
  x: 100,
  y: 100,
  vx: 5, //速度(移動量)
  vy: -5, //速度
  radius: 25,
  color: 'green',
  draw: function () {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color; //塗りつぶしの色
    ctx.fill(); //塗りつぶす
  },
};

function draw() {
  ctx.clearRect(0, 0, anima.width, anima.height);
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;

  // 跳ね返り
  if (ball.y + ball.vy > anima.height || ball.y + ball.vy < 0) {
    ball.vy = -ball.vy; //プラスマイナス反転
  }
  if (ball.x + ball.vx > anima.width || ball.x + ball.vx < 0) {
    ball.vx = -ball.vx;
  }
  raf = window.requestAnimationFrame(draw); //アニメーションの実行
}

//マウスイベントの指定
anima.addEventListener('mouseover', function () {
  raf = window.requestAnimationFrame(draw);
});

anima.addEventListener('mouseout', function () {
  window.cancelAnimationFrame(raf);
});

ball.draw();

draw関数に移動量を追加すると、床でバウンドしているようにすることが出来ます。

ball.x += ball.vx;
ball.y += ball.vy;
ball.vy *= .99;
ball.vy += .25;

draw関数の「clearRect()」を「fillRect()」にすると、後引効果を作ることが出来ます。

ctx.fillStyle = ‘rgba(255, 255, 255, 0.3)’;
ctx.fillRect (0, 0, anima.width, anima.height);

あとがき

MDNのサイトをほぼそのまま記述していますが、コピーするのではなく、実際に自分で書いてみるとコードが何を意図しているのかを理解しやすくなると思います。

かなり時間がかかりますが、ぜひチャレンジしてみてください。

今回も最後までお読み頂きありがとうございました。

タイトルとURLをコピーしました