SVG、画面全体に雪を降らせるアニメーション。

〽️しくみ。 〽️外側のSVG。 〽️画像作成、symbol化。 〽️ランダム発生。 〽️カスタマイズして使おう。





⬆︎SVGついてのまとめページ、CSSアニメーションの基本コーナーはこちら。

こんにちは、「ふ」です。
SVGアニメーション、今回は要素が画面全体を通過していくアニメーションを作ってみましょう。限られたSVG領域で動くのではなく、ユーザがスクロールしても画面から消えません。

当記事執筆は11月末。12月に向けて、「雪」を画面全体に降らせることにしました。完成形は今ご覧のとおりのものです。
SVGで作成しているので、色/大きさその他、細かなカスタマイズは全てコードで行うことができます。

完成系のコードはこちらから。そのままみなさんのwebページに貼り付けて使うことができます。
あ、z-indexだけ調整してくださいね。

クイック目次
(目的のcodeまで飛びます)

しくみ。


position:fixedを使う。


#element { position:fixed; }

CSSのpositionプロパティには、「fixed」という値を指定することができます。

指定された要素は<body>に対して絶対配置となり、スクロールしてもその場から動くことなく表示され続けます。
画面上部にヘッダーを固定したい時などによく使われる手法です。

ここでもし、「fixed」な要素が画面いっぱいの大きさだとしたら。
その中にアニメーションを仕込むと、

ユーザーのスクロールとは関係なく、常にアニメーションが画面上に表示される状態と成増。

この画面いっぱいのfixedに、雪を降らせましょう。

外側のSVG。


画面いっぱいの「fixedな領域」は、SVG要素を使って確保します。
<div>を使っても良いのですが、SVG空間を張ったほうが要素の位置指定がしやすいので、採用しました。

元となるSVG領域。


<body> <svg id = 「「5"soto"」」 width = "100%" 「「1height = "100vh"」」> </svg>  ・・・・・

「height = 100vh」として、画面高さいっぱいのSVG領域を張ります。
このコードは<body>タグのすぐ下に記述しましょう。

〜CSSでポジション関連を指定していきます。

「「5#soto」」 { position:fixed; ..① display:block; ..② overflow:visible; ..③ z-index:5; ..④ background-color:rgba(34,139,34,0.2); ..⑤ 「「1pointer-events:none;」」 ..⑥ }

① ここで「fixed」を指定。topの位置などを特に指定しない限り、要素は<body>の上部に固定されます。

② HTMLでは、svg要素はインライン要素として扱われるのですが、なんか申し訳ないのでblock要素にしました。

③ 後にランダムな位置で雪を降らせるのですが、画像が途切れないようにoverflowを可視状態にしました。

④ SVG領域は他のコンテンツの前面に重なるよう指定。これはお好みで。

⑤ 現在の状態だと画面上は何も変わりません。見た目でわかるよう、一時的に少し色を付けておきます。forestgreenを透過したもの「rgba( 34,139,34,0.2 )」にしました。



これ⬇︎をわすれないで!

「pointer-events:none」を必ず指定しましょう
画面いっぱいのSVG領域を前面に貼り付けるため、指定しないままでは背面にあるリンク要素などがクリックできなくなっています。
指定することでSVG領域のクリックは無効になり、背面にある要素が(クリックなどの)ターゲットとして有効に成増。

コードを実行してみると、画面全体をSVG領域が覆っているのがわかります。なおかつ、コンテンツのスクロールとは独立していますね。

画像作成、symbol化。


アニメーションさせる雪の画像ですが、直接使用するのではなく一度<symbol>要素として定義化し、<use>要素で参照する形を取るようにします。
<use>要素には水平位置を指定する「x属性」を使うことができます。のちに雪をランダム発生させるのですが、transformを使わなくても水平移動が可能になるので、とても便利です。 先づはフツーに雪1粒の画像を作成し、画面上から下へと落下するアニメーションを付けてみましょう。

雪1粒の画像。


<svg id = "soto" width = "100%" height = "100vh"> 「「3<!-- グラデーションを定義 -->」」 <radialGradient id = 「「5"tama"」」 cx = "60%" cy = "40%" r = "80%"> <stop offset = "0" style = "stop-color:#fff"/> <stop offset = "0.5" style = "stop-color:#eee"/> </radialGradient> 「「3<!-- 円を作り、定義したグラデーションで塗りつぶし -->」」 <circle id = 「「1"yuki"」」 cx = "30%" cy = "10%" r = "25" fill = "url「「5(#tama)」」"/> </svg>

ただの円なので、コードだけで作成しました。
fill(塗りつぶし)にはグラデーション「#tama」を定義して適用させています。グラデーションは完全な白(#fff)とほんのりグレー(#eee)の2色で、cxとcy属性を使って中心を少しずらしています。

circle(円)要素にはid名「yuki」を指定しておきました。このあとCSSで、ぼかしとアニメーションを付けます。

SVGのグラデーションについて詳しく知りたい方は、こちらの記事⬇︎をご覧ください。

SVGのグラデーションについて。

2021.10.23
カラーストップ/オフセットとは?


ぼかしとアニメーションのCSS。


「「1#yuki」」 { 「「3/* ぼかし */」」 filter:blur(4px); 「「3/* アニメーション */」」 animation:yuki 4s linear 「「4forwards」」; } @keyframes yuki { from { transform:translateY(-20vh); } to { transform:translateY(120vh); } }

CSSのfilter要素でぼかし(blur)を付けました。

アニメーションの内容ですが、translateYで上から下へと移動させます。
単位は「vh」とし、開始時は-20vh(画面上外20%)、終了時は120vh(画面下外20%)にしています。これで画面の外から入って外へ出る、という動きを表現できます。

またデフォルトの状態では、アニメーション終了後は要素が最初の位置、つまり画面内に戻ってきてしまいます。
要素が画面の外に消えた状態でアニメーションを終了させたい。そのため、animation-directionの値を「forwards」に指定しましょう。

雪1粒が画面を通過していくアニメーションが実装できました。
(*画像では見やすいようにアニメーションを繰り返しています)

symbol要素化する。


<symbol id = 「「5"sansyou"」」> <circle cx = "30%" cy = "10%" r = "25" fill = "url(#tama)"/> </symbol> <use id = 「「1"yuki"」」 href = 「「5"#sansyou"」」/>

動作が確認できたら、<symbol>と<use>要素の関係になるようコードを⬆︎のように変更します。
circle(円)のデータを<symbol>で囲んで「定義化」し、id名「sansyou」としました。そのあと<use>要素で呼び出しています。最初に付けていたid「yuki」はuseのほうにお引っ越し。

こうすることで、水平位置をx属性だけでコントロールすることができます。次のセクションでそのメリットが明らかに成増。

ランダム発生。


それではSVG領域内で、雪のアニメーションをランダムに発生させていきます。
先ほど外側のSVG領域に指定した背景色(forestgreen)はキャンセルしておきましょう。

ランダムな水平位置で発生させる。


「「3//外側のSVG領域と雪を取得 ...①」」 const soto = document.getElementById("soto"); const yuki = document.getElementById("yuki"); 「「3//リミッター用の変数 ...②」」 let count = 0; 「「3//use要素を複製して追加 ...③」」 function tuika() { 「「3//複製 ...④」」 let clone = yuki.cloneNode(true); 「「3//アニメーション終了後に削除 ...⑤」」 clone.addEventListener("animationend",function() { clone.remove(); }); 「「3//水平位置をランダムにするための変数 ...⑥」」 let iti = String(Math.random()*200-100)+"%"; 「「3//⑥を雪のx属性に適用 ...⑦」」 clone.setAttribute("x",iti); 「「3//SVG領域にcloneを追加 ...⑧」」 soto.appendChild(clone); 「「3//リミッター変数を増やす ...⑨」」 count++; } 「「3//一定時間ごとに実行 ...⑩」」 const limit = setInterval(function() { tuika(); 「「3//規定回数を超えると実行中止 ...⑪」」 if(count>50) { clearInterval(limit); } },200);


① SVG領域と雪要素を取得します。

② ユーザがページ閲覧中、最初から最後まで雪が降っているとちょっとクドイ。
ので、規定回数に達したら雪が止むようにします。そのためのカウンター変数をおきました。

③ 複製・ランダム発生させるための関数「tuika」を定義。

④ cloneNodeメソッドで雪を複製。引数はいちおうtrue(子要素も一緒に複製する)にしておきました。

⑤ 複製・追加した雪要素を、アニメーション終了後に削除するためのイベントリスナです。


⑥ 水平位置に指定する数値をランダムに生成し、文字列化しています。
Math.randomの使い方については、こちら⬇︎の記事を参考にしてください。

JavaScript、乱数の範囲や重複を指定〜Math.random使い方。

2021.11.17
整数/範囲/重複の有無を自在にあやつろう。



⑦ ⑥で生成した値をcloneのx属性に指定します。
画像作成時、雪要素にはあえて<use>要素を使いました。<use>要素には水平位置である「x属性」を指定することができるため、位置決めに関してはアニメーションで使いたいtransformプロパティと切り離して扱うことができます。

⑧ 複製したものをSVG領域に追加します。

⑨ 関数が実行される度、リミッターの値を1つ増やします。

⑩ 一定時間ごとにtuika()関数を実行させます。インターバルは200msとしました。

⑪ setInterval内部に、規定回数を超えると実行を中止する条件を加えます。先ほどのcount変数がもとに成増。ここでは50回をリミットに設定しました。

完成。




これで完成です。画面全体に雪が降っていますね ♪
お疲れ様でした!

カスタマイズして使おう。


全コード。


<svg id = "soto" width = "100%" height = "100vh"> <radialGradient id = "tama" cx = "60%" cy = "40%" r = "80%"> <stop offset = "0" style = "stop-color:#fff"/> <stop offset = "0.5" style = "stop-color:#eee"/> </radialGradient> <symbol id = "sansyou"> <circle cx = "30%" cy = "10%" r = "25" fill = "url(#tama)"/> </symbol> <use id = "yuki" href = "#sansyou"/> </svg> <script> const soto = document.getElementById("soto"); const yuki = document.getElementById("yuki"); let count = 0; function tuika() { let clone = yuki.cloneNode(true); clone.addEventListener("animationend",function() { clone.remove(); }); let iti = String(Math.random()*200-100)+"%"; clone.setAttribute("x",iti); soto.appendChild(clone); count++; } const limit = setInterval(function() { bbtuika(); bbif(count>50) { bbclearInterval(limit); bb} },200); </script> <style> #soto { display:block; position:fixed; overflow:visible; z-index:5; pointer-events:none; } #yuki { animation:yuki 4s linear forwards; filter:blur(4px); } @keyframes yuki { from { transform:translateY(-20vh); } to { transform:translateY(120vh); } } </style>

⬆︎今回作成した「画面全体に雪を降らせるSVGアニメーション」の全コードです。

コピーして、HTMLファイルの<body>直下に貼るだけで、画面全体に雪が降ります。ただしご自身のサイトでz-indexを操作している場合には、値を調整してください。

今回のコードは「さまざまなプロパティや属性をカスタマイズできる」ことを意識して作成しました。
要素の色や大きさ、アニメーションのタイミングなどをみなさんのお好みでカスタマイズしてみてください。たとえば、z-indexを使ってコンテンツの背面にセットすると、また違った印象になることでしょう。

最後までお読みくださり、ありがとうございました。
これから寒くなってくる(←執筆当時)ので、お体に気をつけてください。

ではまた〜 ♪



!--ひとかたまり (hsl変換) -->

HSL変換を使った配色や色指定。

2021.09.24
配色パターンを秒作。


CSSアニメーション、中間テスト。

2021.06.03
基本編を終えたらチャレンジしましょう。












「ふ」です。

swift、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。