今回は簡単なアニメーションについて見ていきます。
ご紹介している内容は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のサイトをほぼそのまま記述していますが、コピーするのではなく、実際に自分で書いてみるとコードが何を意図しているのかを理解しやすくなると思います。
かなり時間がかかりますが、ぜひチャレンジしてみてください。
今回も最後までお読み頂きありがとうございました。

