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