前提
参考元
Moon Night Meteor Canvas Animation
上記リンク先はCanvasを用いたアニメーション作成動画で、HTMLとJavaScriptを使用して、月と煌めく星と流れ星のアニメーションが表示されます。
動画ではDOMContentLoadedで制御されていますが、これをkeydownが発生すると流れ星のアニメーションが開始されるようなコードを構築しようとしています。(流れ星以外のアニメーションはページロード時に表示)
実現したいこと・要点
- キーボード押下一回につき、表示される流れ星は一つ。2回3回押せば2個3個表示される。
- 流れ星のアニメーションは、画面外上部から始まり、画面外下部へ消えて終了がワンループ。
- 生成された流れ星のスピードは、画面外へ消えるまで常に一定(生成時に決められたスピードの値が変化しないということ)
- 流れ星の発生位置、スピードともにランダム。
発生している問題・エラーメッセージ
- 連続したキーダウンに対応できていない。連続すると(恐らく)配列に次々と追加され、動作が重くなる。
- 流れ星のアニメーションが途中で終了してしまう。これにより、次のアニメーション開始位置が途中で終了した地点からとなり、望む動作になっていない。
該当のソースコード
HTML,JavaScript
1<!DOCTYPE html> 2<html> 3 <head> 4 <meta charset="UTF-8"> 5 <link rel="stylesheet" href="Moon.css"> 6 </head> 7 <body> 8 <canvas id="canvas"></canvas> 9 10 <script> 11 let canvas, ctx, w, h, moon, meteor; 12 let count = 0; 13 let stars = []; 14 let meteors = []; 15 16 function init() { 17 canvas = document.querySelector("#canvas"); 18 ctx = canvas.getContext("2d"); 19 resizeReset(); 20 moon = new Moon(); 21 for (let a = 0; a < w * h * 0.0001; a++) { 22 stars.push(new Star()); 23 } 24 // for (let b = 0; b < 7; b++) { 25 // meteors.push(new Meteor()); 26 // } 27 StarAnimationLoop(); 28 } 29 30 const countUp = () => { 31 if (count < 80) { 32 count++; 33 console.log(count); 34 drawMeteorScene(); 35 requestAnimationFrame(countUp); 36 } 37 } 38 39 function keydown() { 40 count = 0; 41 meteor = new Meteor(); 42 meteors.push(meteor); 43 countUp(); 44 // MeteorAnimationLoop(); 45 // if (meteor.y >= h + 50) { 46 // cancelAnimationFrame(MeteorAnimationLoop); 47 // } 48 } 49 50 function resizeReset() { 51 w = canvas.width = window.innerWidth; 52 h = canvas.height = window.innerHeight; 53 } 54 55 function StarAnimationLoop() { 56 ctx.clearRect(0, 0, w, h); 57 drawStarScene(); 58 requestAnimationFrame(StarAnimationLoop); 59 } 60 61 function MeteorAnimationLoop() { 62 drawMeteorScene(); 63 // requestAnimationFrame(MeteorAnimationLoop); 64 } 65 66 function drawStarScene() { 67 moon.draw(); 68 stars.map((star) => { 69 star.update(); 70 star.draw(); 71 }) 72 } 73 74 function drawMeteorScene() { 75 meteors.map((meteor) => { 76 meteor.update(); 77 meteor.draw(); 78 }) 79 } 80 81 class Moon { 82 constructor() { 83 this.x = 150; 84 this.y = 150; 85 this.size = 100; 86 } 87 draw() { 88 ctx.save(); 89 ctx.beginPath(); 90 ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); 91 ctx.shadowColor = "rgba(254, 247, 144, .5)"; 92 ctx.shadowBlur = 50; 93 ctx.fillStyle = "rgba(254,247,144, 1)"; 94 ctx.fill(); 95 ctx.closePath(); 96 ctx.restore(); 97 } 98 } 99 100 class Star { 101 constructor() { 102 this.x = Math.random() * w; 103 this.y = Math.random() * h; 104 this.size = Math.random() + 0.5; 105 this.blinkChance = 0.05; 106 this.alpha = 1; 107 this.alphaChange = 0; 108 } 109 draw() { 110 ctx.beginPath(); 111 ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); 112 ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`; 113 ctx.fill(); 114 ctx.closePath(); 115 } 116 update(){ 117 if (this.alphaChange === 0 && Math.random() < this.blinkChance) { 118 this.alphaChange = -1; 119 } else if (this.alphaChange !== 0) { 120 this.alpha += this.alphaChange * 0.5; 121 if (this.alpha <= 0) { 122 this.alphaChange = 1; 123 } else if (this.alpha >= 1) { 124 this.alphaChange = 0; 125 } 126 } 127 } 128 } 129 130 class Meteor { 131 constructor() { 132 this.reset(); 133 } 134 reset() { 135 this.x = Math.random() * w; 136 this.y = -100; 137 this.size = Math.random() * 2 + 0.5; 138 this.speed = (Math.random() + 1) * 5; 139 } 140 draw() { 141 ctx.save(); 142 ctx.strokeStyle = "rgba(255, 255, 255, .1)"; 143 ctx.lineCap = "round"; 144 ctx.shadowColor = "rgba(255, 255, 255, 1)"; 145 ctx.shadowBlur = 10; 146 for (let i = 0; i < 10; i++) { 147 ctx.beginPath(); 148 ctx.moveTo(this.x, this.y); 149 ctx.lineWidth = this.seze; 150 ctx.lineTo(this.x + 10 * (i + 1), this.y - 10 * (i + 1)); 151 ctx.stroke(); 152 ctx.closePath(); 153 } 154 ctx.restore(); 155 } 156 update() { 157 this.x -= this.speed; 158 this.y += this.speed; 159 if ( this.y >= h + 50) { 160 this.reset(); 161 } 162 } 163 } 164 165 window.addEventListener("DOMContentLoaded", init); 166 window.addEventListener("keydown", keydown); 167 window.addEventListener("resize", resizeReset); 168 </script> 169 </body> 170</html>
css
1body { 2 padding: 0; 3 margin: 0; 4 height: 100vh; 5 background: linear-gradient(0deg,rgba(13,35,93,1) 0%,rgba(0,5,8,1) 70%);/*グラディエーションの角度と色 */ 6} 7 8#canvas { 9 position: fixed; 10 left: 0; 11 top: 0; 12 width: 100%; 13 height: 100%; 14}
試したこと
- keydown 関数内で countUp 関数を使用してキーダウンによるアニメーションの制御を試みましたが、連続したキーダウンに対応できず、アニメーションが重くなる問題が発生しています。
- 流れ星のアニメーション終了後の制御に関して、MeteorAnimationLoop などを試しましたが、適切なアプローチが見つかっていません。
補足情報
- 現在のコードは、ページロード時には月と煌めく星が表示され、キーダウンイベントが発生すると流れ星のアニメーションが開始されるが、問題点が発生しています。
- 連続したキーダウンに対応し、流れ星のアニメーションを滑らかに制御できるような方法を模索しています。
- 流れ星のアニメーションが途中で終了してしまい、次のアニメーション開始位置が望ましくないため、改善策を探しています。