質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
canvas

HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

Q&A

1回答

780閲覧

JavaScriptで流れ星のアニメーションを適切に制御する。

usuake

総合スコア0

canvas

HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

0グッド

2クリップ

投稿2023/12/03 06:12

編集2023/12/06 03:04

前提

参考元
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 などを試しましたが、適切なアプローチが見つかっていません。

補足情報

  • 現在のコードは、ページロード時には月と煌めく星が表示され、キーダウンイベントが発生すると流れ星のアニメーションが開始されるが、問題点が発生しています。
  • 連続したキーダウンに対応し、流れ星のアニメーションを滑らかに制御できるような方法を模索しています。
  • 流れ星のアニメーションが途中で終了してしまい、次のアニメーション開始位置が望ましくないため、改善策を探しています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

umimi

2023/12/03 09:43

Moon.css は、公開されないんですか ?
usuake

2023/12/06 03:02

申し訳ありません。 Moon.cssでは、背景色やオブジェクトの位置などを設定しているだけで、アニメーションの動作には関係ないと思い公開していませんでした。 編集後公開しておきます。
guest

回答1

0

いまいち要件がはっきりしませんが、アニメーション中かどうかを保存する変数を作っておけばよいのではないでしょうか。

下記は、ページ読込後は流れ星のアニメーションは始まっていませんが
キーを押すと、(指定した個数以下の)流れ星のアニメーションが開始します。
流れ星のアニメーションが表示されている状態でもう一度キーを押すと、表示中の各流れ星が全部プログラム上画面外に消えた時点でそれ以上流れなくなります。
流れ星が1つも流れていない状態でキーを押すと、アニメーションが再開します。(画面上部から流れ星が表示されます)
アニメーション中に連続してキーを押しても、そのキーに応じてアニメーションの表示・停止が切り替わり、表示される流れ星の最大個数は変わらず、動作が重くなることもありません。
なお、星のまたたきは、流れ星と関係なくアニメーションするものとしています。

もし動作がイメージと異なる場合は、上記にように分かりやすく具体的に要件を説明してください。

注)元のコードからの変更が最小限になるようにしています。グローバル変数が多用されていたりするのはそのためです。

js

1let canvas, ctx, w, h, moon, stars = [], meteors = []; 2let isMeteorAnimationEnabled = false; 3function init() { 4 canvas = document.querySelector("#canvas"); 5 ctx = canvas.getContext("2d"); 6 resizeReset(); 7 moon = new Moon(); 8 for (let a = 0; a < w * h * 0.0001; a++) { 9 stars.push(new Star()); 10 } 11 12 animationLoop(); 13} 14 15function keydown() { 16 if (isMeteorAnimationEnabled) { 17 isMeteorAnimationEnabled = false; 18 } else { 19 for (let b = 0; meteors.length < 2; b++) { 20 meteors.push(new Meteor()); 21 } 22 isMeteorAnimationEnabled = true; 23 } 24} 25function resizeReset() { 26 w = canvas.width = window.innerWidth; 27 h = canvas.height = window.innerHeight; 28} 29 30function animationLoop() { 31 ctx.clearRect(0, 0, w, h); 32 drawScene(); 33 requestAnimationFrame(animationLoop); 34} 35 36function drawScene() { 37 moon.draw(); 38 stars.map((star) => { 39 star.update(); 40 star.draw(); 41 }); 42 meteors.map((meteor) => { 43 meteor.update(); 44 meteor.draw(); 45 }); 46} 47 48class Moon { 49 constructor() { 50 this.x = 150; 51 this.y = 150; 52 this.size = 100; 53 } 54 draw() { 55 ctx.save(); 56 ctx.beginPath(); 57 ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); 58 ctx.shadowColor = "rgba(254, 247, 144, .7)"; 59 ctx.shadowBlur = 70; 60 ctx.fillStyle = "rgba(254, 247, 144, 1)"; 61 ctx.fill(); 62 ctx.closePath(); 63 ctx.restore(); 64 } 65} 66 67class Star { 68 constructor() { 69 this.x = Math.random() * w; 70 this.y = Math.random() * h; 71 this.size = Math.random() + 0.5; 72 this.blinkChance = 0.005; 73 this.alpha = 1; 74 this.alphaChange = 0; 75 } 76 draw() { 77 ctx.beginPath(); 78 ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); 79 ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`; 80 ctx.fill(); 81 ctx.closePath(); 82 } 83 update() { 84 if (this.alphaChange === 0 && Math.random() < this.blinkChance) { 85 this.alphaChange = -1; 86 } else if (this.alphaChange !== 0) { 87 this.alpha += this.alphaChange * 0.05; 88 if (this.alpha <= 0) { 89 this.alphaChange = 1; 90 } else if (this.alpha >= 1) { 91 this.alphaChange = 0; 92 } 93 } 94 } 95} 96 97class Meteor { 98 constructor() { 99 this.reset(); 100 } 101 reset() { 102 this.x = Math.random() * w + 300; 103 this.y = -100; 104 this.size = Math.random() * 2 + 0.5; 105 this.speed = (Math.random() + 0.5) * 15; 106 } 107 draw() { 108 ctx.save(); 109 ctx.strokeStyle = "rgba(255, 255, 255, .1)"; 110 ctx.lineCap = "round"; 111 ctx.shadowColor = "rgba(255, 255, 255, 1)"; 112 ctx.shadowBlur = 10; 113 for (let i = 0; i < 10; i++) { 114 ctx.beginPath(); 115 ctx.moveTo(this.x, this.y); 116 ctx.lineWidth = this.size; 117 ctx.lineTo(this.x + 10 * (i + 1), this.y - 10 * (i + 1)); 118 ctx.stroke(); 119 ctx.closePath(); 120 } 121 ctx.restore(); 122 } 123 update() { 124 if (this.y < h + 100) { 125 this.x -= this.speed; 126 this.y += this.speed; 127 } else if (isMeteorAnimationEnabled) { 128 this.reset(); 129 } 130 } 131} 132 133window.addEventListener("DOMContentLoaded", init); 134window.addEventListener("keydown", keydown); 135window.addEventListener("resize", resizeReset);

styles.css

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} 15

index.html

html

1<canvas id="canvas"></canvas>

投稿2023/12/03 10:11

編集2023/12/03 10:41
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

usuake

2023/12/06 03:32

要点が分かりづらく申し訳ありませんでした。以下に要点を記述させていただきます。(また分かりづらかったら言って下さい。) - キーボード押下一回につき、表示される流れ星は一つ。2回3回押せば2個3個表示される。 - 流れ星のアニメーションは、画面外上部から始まり、画面外下部へ消えて終了がワンループ。 - 生成された流れ星のスピードは、画面外へ消えるまで常に一定(生成時に決められたスピードの値が変化しないということ) - 流れ星の発生位置、スピードともにランダム。 になります。 SaYGOさんのコードは一度keydownが発生すると決められた個数の流れ星が、画面上部から画面下部へ流れていくアニメーションが繰り返されるコードになっています。これは私が望む挙動ではありません。流れ星が生成されて、一度画面外へ消えたら、そこで終了してほしいと思っています。 また、SaYGOさんのコードは、「keydown発生→流れ星→再度keydown→アニメーションがストップ→再々度keydown→流れ星」という流れになっていますがこれを、「keydown発生→流れ星→再度keydown→流れ星」という流れにしたいです。「アニメーションがストップ」という手間?ラグ?を無くしたいと考えています。
umimi

2023/12/06 09:56

SaYGOさんのコードを参考にして、御自分で修正すれば、いいじゃないですか ? なぜ、SaYGOさんにやらせようとするの ? 「自分で修正したけど、うまくいきません」なら、ともかく。 teratail は、無料のプログラム作成所じゃない。 できないのなら「金を払って」外注すべき。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問