雨のアニメーションをSVGで作ろう。

〽️雨を1つ作る。 〽️ランダム発生。 〽️記事に重ねる。 〽️季節化させたいですね。





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

こんにちは、「ふ」です。
今回のSVGアニメーションでは「雨」を降らせてみましょう。

ただ雨のアニメーションを作るだけでなく、〜季節はもうすぐ6月(執筆当時)。webページの上から雨を降らせて、季節感を醸しだそうと思います。

そう、こんな感じで。ふふふ。







雨を1つ作る。


先づは「雨」を1つ作りましょう。

ベクターソフトで、この⬆︎ような画像を作成しました。

 ・雫(縦のライン)
 ・波紋(楕円)
 ・変形の基準を取るための円(青)

3つ目の青い円の中心座標を抽出し、雫や波紋のアニメーションの基準点に利用します。
これをSVG形式で書き出し、htmlの <body> 〜 </body> に流し込みましょう。



<svg viewBox="0 0 841.89 595.28"> 「「3<!--波紋-->」」 <ellipse 「「1id = "hamon"」」 class="st1" cx="420.94" cy="547.29" rx="99.84" ry="33.06"/> 「「3<!--雫-->」」 <line 「「1id = "sizuku"」」 class="st1" x1="420.9" y1="350.3" x2="420.9" y2="547.3"/> 「「3<!--変形の基準、コメントアウト。-->」」 「「3<!--<circle class="st2" 「「4cx="420.94" cy="547.29"」」 r="15.88"/>-->」」 </svg>

<body>内に流し込んだコード(インライン表記に不要な部分は略しています)に手を加えていきます。
波紋と雨本体のオブジェクトにはid名「hamon」「sizuku」を指定しました。
また「変形の基準用の円」はコメントアウトして表示させず、中心の座標だけをこの後利用します。

基準用の青い円はコメントアウトしたため、現在のブラウザ表示はこのようになっています。
雫が上から落ちてきて、水面に触れると同時に波紋が広がり出すアニメーションを仕込みます。

■ CSS

#hamon,#sizuku { bbtransform-origin:50% 91.93%; bb} 

変形の基準点は雫が波紋に当たる部分、つまり先程コメントアウトした「青い円」の中心ですね。そこの座標はSVGで書き出したので取得できています。なおかつCSSで「%」指定にします。
その計算式が、

基準点(%) ≒ {(円の中心座標)/(viewBoxの最大値)} * 100

小数点以下2桁くらいの精度で指定すれば、見た目違和感なく表示されます。
CSSの記述を続けます。

#hamon { animation:hamon 1.4s linear infinite; } @keyframes hamon { from { transform:scale(0); } 50% { transform:scale(0); } } #sizuku { animation:sizuku 1.4s linear infinite; } @keyframes sizuku { from { transform:translateY(-80%) scaleY(1); } 50% { transform:translateY(0%) scaleY(1); } 100% { transform:translateY(0%) scaleY(0); } }

キーフレームですが、進捗50%のところで雫が水面に達し、波紋が広がっていくようにしました。



結果がこちら⬆︎です。
1つの「雨」が完成しました。





SVGアニメーション、 回転の中心を指定する。

2021.03.09
座標を取得して%変換。

変形の基準点を求める小技については、こちら⬆︎の記事で詳しく紹介しています。参考にしてください。

ランダム発生。


作成したSVGアニメーションの外側にSVG領域を定義し、その中で先程のものをランダムに発生させます。

「「3<!--外側のSVGを定義-->」」 「「1<svg 「「5id = "soto"」」 viewBox="0 0 841.89 595.28">」」 「「3<!--内側SVG(雨)-->」」 <svg 「「5id = "ame"」」 viewBox="0 0 841.89 595.28">  〜略〜 </svg> 「「1</svg>」」

先づは雨のアニメーションの外側をSVGで囲みましょう。
外側SVGのid名を「soto」、viewBoxは内側と同じものにしました。また、内側SVGはid名「ame」としています。

このあとJavaScriptで内側の「ame」をランダムに発生させるのですが、1つ1つの「ame」はアニメーション終了後に削除されるようにしましょう。でないと無限増殖してしまうことに成増。
そのため先程のアニメーションではプレビューしやすいように繰り返し(animation-iteration-count)を「infinite」にしていましたが、指定を外して「1回のみの再生」にしておきます。

■ CSS

「「3<!--「infinite」を外す-->」」 #hamon { animation:hamon 1.4s linear; } #sizuku { animation:sizuku 1.4s linear; }

準備ができたら、JavaScriptを使ってランダム発生させましょう。

■ JavaScript

「「3//① 内外のSVGを取得」」 const ame = document.getElementById("ame"); const soto = document.getElementById("soto"); 「「3//② 元の「ame」を可視領域外に逃がす」」 ame2.setAttribute("x","-400%"); 「「3//③ 「ame」を複製→削除する関数」」 function random() { 「「3//④ 複製」」 let clone = ame.cloneNode(true); 「「3//⑤ アニメーション終了後に削除」」 clone.children[1].addEventListener("animationend",function() { bbclone.remove(); }); 「「3//⑥ 水平成分をランダムに指定」」 let horizon = String(Math.random()*100-50)+"%"; clone.setAttribute("x",horizon); 「「3//⑦ 「soto」に追加」」 soto.appendChild(clone); } 「「3//⑧ 一定周期で繰り返し」」 setInterval(random,500);



① 内側「ame」と外側「soto」をそれぞれ取得します。

② 元からある「ame」を残しておくと、常に中央に雨が落ちる状態と成増。これでは「ランダム」とは言えないので、x属性を指定して可視領域の外側に逃がしておきましょう。

③ 「ame」を複製して「soto」に追加し、アニメーション終了後に削除する関数です。内容を見ていきましょう。

④ cloneNodeメソッドで「ame」を複製。

⑤ アニメーション終了イベントを検知して複製した「ame」を削除します。
ただしアニメーションしているのは「ame」の子要素である「#sizuku」や「#hamon」なので、そこに対してイベントハンドラ。

ちなみにベクターソフトから書き出したSVGには、描画要素の前にスタイルシートなどの別の子要素が含まれています。children[0]とした場合には書き出したSVGのスタイルシートを参照してしまう場合があります。
何番目の子要素が「#sizuku」や「#hamon」なのか、console.logして確認しながらイベントハンドラを与えてください。



⑥ 発生した「ame」の水平位置をランダムにしています。
「ame」と「soto」はviewBoxは同一で、アニメーションしている子要素は水平方向中央に位置しています。-50〜+50%の範囲に分布させることで、領域内に均一に発生させることができます。

ランダム配置について、詳しくはこちら⬇︎の記事を参考にしてください。


SVG アニメーション、ランダムにキラキラ。

2021.04.25
〜SVG in SVG。






⑦ appendChildを使って複製した「ame」を「soto」に追加。

⑧ 定義した関数random()を一定周期で実行させます。

結果このようになりました。領域内にランダムに雨が落ちています。
当初はY方向にもある程度分布させようと思っていたのですが、X方向だけでもそれっぽくなってくれました。

記事に重ねる。


それでは完成したSVGアニメーションを、ブログ記事の上に重ねていきましょう。
しかしここで単純に「position:absolute」にすると、SVGアニメーションが常に追尾することになってしまいます。
〜追尾型広告、うざいですよね💧

ページの中で一定の範囲だけ追尾して、それ以上スクロールすると画面上に消えていく、というのはどうでしょうか?それならそんなに鬱陶しくないかも、です。

「追尾させる記事範囲」とSVGアニメーションをdiv領域で囲み、SVGには「ヘッダー固定」などでよく使う「position:sticky」を指定します。

■ HTML

「「1<div id = "tracking">」」 <svg id = "soto" ...>  〜略〜 </svg> 「「4<div id = "range">」」  〜追尾させたい範囲のhtmlをここに置く〜 「「4</div>」」 「「1</div>」」

SVGと追尾範囲をid「tracking」で囲み、追尾範囲をid「range」としました。
CSSを記述します。

■ CSS

#soto { position:sticky; top:0; } #range { margin-top:-20em; }

追尾範囲が雨と重なるよう、マイナスのmargin-topを指定して上に引き上げています。




結果はこのように。
ある程度までは雨が追いかけてきますが、それ以降スクロールすると画面の上に消えていきます。

皆さんも期間限定で、ご自分のサイトを着飾ってみては?





季節化させたいですね。


最後までお読みくださり、ありがとうございました。
今回はSVGアニメーションを記事の上に重ねるという、新しい試みでした。少し透明にしてみたりして文章が読みづらくならない程度であれば、なかなか面白い効果が得られると思います。

記事を執筆したのが5月末だったので「雨」にしてみましたが、他にも色々試せそうです。いっそ「季節のSVGアニメーション」としてシリーズ化しようかな・・
ニーズがありそうな、なさそうな笑

ではまた〜 ♪



SVGでボタンアニメーション【Web Animations API】。

2020.12.19
event.targetからオブジェクトを動かす。

SVGアニメーション、裏表を見せながら回転。

2020.10.09
〽️ X軸を基準に回転。


SVGアニメーション、 回転スワイプで画像切り替え。

2021.02.27
ポイントは「2重クリップ」。












「ふ」です。

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