雪の背景アニメーション
- Digital0 JPY

# 雪の背景アニメーション — コード解説 ## 概要 `index.html` は、画面全体に降り積もる雪のアニメーションを表示する単一ファイルのWebページです。外部ライブラリを使わず、2枚の `<canvas>` を重ねてバニラJavaScriptで実装されており、雪の結晶が画面上部から落下し、左右に揺れながら回転し、地面に到達すると徐々に消えていく雪だまりとして積もる様子を再現しています。画面をクリックすると、その位置に追加の雪が発生します。 このページ自体には表示コンテンツがなく、装飾的な背景レイヤーとして使うことを想定しています(例:サイトの他のコンテンツの背後に重ねる)。両方のキャンバスは `position: fixed` で画面全体を覆い、`pointer-events: none` が設定されているため、下にあるコンテンツの操作を妨げません。 ## ドキュメント構造 ```html <canvas id="groundCanvas"></canvas> <canvas id="snowCanvas"></canvas> ``` - **`snowCanvas`** — 現在落下中の雪を描画するキャンバス。 - **`groundCanvas`** — 地面に到達し、徐々にフェードアウトしていく雪を描画するキャンバス。 両キャンバスは `z-index: 5` で重なり、`width: 100%` / `height: 100dvh` で画面全体を覆い、`position: fixed` で左上に固定されています。`<body>` の `overflow: hidden` により、フルスクリーンのキャンバスによってスクロールバーが表示されるのを防いでいます。 ## 状態管理 アニメーションの状態は2つの配列で管理されています。 | 変数 | 用途 | |---|---| | `leaves` | 現在落下中の雪(位置、速度、揺れ、回転、色、形状タイプ) | | `groundLeaves` | 地面に積もりフェードアウト中の雪(最大50件、古いものから削除) | ## 主な関数 **`getScrollProgress()`** ページのスクロール量を0〜1の値として算出します。計算はされていますが、現在は見た目に反映されていません(詳細は下記「気づいた点」を参照)。 **`getSnowColor()`** `rgba(180, 220, 255, 不透明度)` という薄い青みがかった白色の文字列を返します。不透明度は0.6〜0.9の範囲でランダムに決まり、雪の結晶ごとに透明感に差が出ます。 **`resizeCanvas()`** 両キャンバスのピクセルサイズを現在のウィンドウサイズに合わせます。初回読み込み時と、`resize` イベント発生時に実行されます。 **`createSnow(x, y)`** `leaves` 配列に新しい雪を追加します。座標を指定しない場合、横方向の発生位置はランダムですが、画面幅の中央60%を避けるように偏らせています(雪が画面の左右端に寄って降るため、中央部分は空きやすくなります)。各雪には、サイズ・落下速度・横揺れ(振幅と速度)・回転と回転速度・`getSnowColor()` による色・描画形状を決める `type` 値(0〜1)がランダムに割り当てられます。 **`addGroundSnow(x, y, color, size, type)`** 落下し終えた雪のデータを `groundLeaves` に移し、初期不透明度0.5とランダムなフェード速度を与えます。これにより `groundCanvas` 上で徐々に消えていく積雪として描画されます。配列は最大50件までに制限されています。 **`drawSnow(ctx, x, y, size, rotation, color, type)`** 1つの雪の結晶を描画します。`type` の値に応じて3種類の見た目から選択されます。 - `type < 0.3` — 中心に点を持つシンプルな六角形の輪郭。 - `type < 0.7` — 1段の斜めの側枝を持つ6方向の結晶。 - `type >= 0.7` — 複数段の側枝・副枝・先端装飾・中心の小さな六角形を持つ、より複雑な6方向の結晶。 いずれも、描画コンテキストを雪の位置・角度に合わせて平行移動・回転させたうえで、パスの描画・線・塗りつぶしで表現されています。 **`updateLeaves()`** 毎フレーム実行されます。`snowCanvas` をクリアし、一定確率(6%)で新しい雪を発生させ(同時に存在する雪は最大40個まで)、既存の雪それぞれについて、落下(`y += speed`)、サイン波を使った横揺れの更新(`x = initialX + sin(swayOffset) * swayAmount`)、回転の更新を行います。キャンバス高さの95%を超えた雪は `leaves` から取り除かれ、`addGroundSnow()` に渡されます。 **`updateGroundLeaves()`** 毎フレーム実行されます。`groundCanvas` をクリアし、地面に積もった各雪の不透明度を `fadeSpeed` 分だけ減らし、完全に消えたものを削除、残りを(元のサイズの80%、白一色に不透明度を上書きして)再描画します。 **クリックイベント処理** `click` イベント発生時、クリック位置付近に3つの雪を追加で発生させます。100ms間隔で `setTimeout` により順に発生し、それぞれ横方向に±25pxのランダムなずれが加えられます。 **`animate()`** メインループです。`requestAnimationFrame` により毎フレーム `updateLeaves()` と `updateGroundLeaves()` を呼び出し、アニメーション全体を継続的に駆動します。 ## 気づいた点 - **`getScrollProgress()` は実質的に未使用です。** `getSnowColor()` は `scrollProgress` 引数を受け取り `progress` を計算していますが、その値は不透明度の計算には一切使われていません。不透明度は単純に `0.6 + Math.random() * 0.3` で決まります。これは、以前のバージョン(例:スクロール位置に応じて色や不透明度が変化する仕様)の名残で、簡略化された際に完全には整理されなかったコードと思われます。 - **中央を避ける発生ロジック** により、`createSnow()` では雪が画面の左右3分の1あたりに視覚的に集中します。これは、この背景を中央寄せのページコンテンツの背後に配置する場合に有用な挙動です。 - **個数上限**により、パフォーマンスが一定範囲に保たれています(落下中の雪は最大40個、積もった雪は最大50個まで)。 - **外部依存なし** — HTML5 Canvas APIとバニラJavaScriptのみで構成されており、ビルド不要でブラウザで直接開けます。 - **ソースコード内のコメントは日本語**で書かれており、各処理(雪の生成、色のロジック、描画ロジック、クリック処理など)の役割が説明されています。
