CSS+JavaScript、スクロールに合わせて要素をふわっと浮かばせる。

〽️Jquery不使用。 〽️JavaScriptは..💧でも実装可能です。 〽️複数や時間差にも対応。




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



syntax。


#elm1 { opacity:0; transform:translateY(50%); } .huwa { animation:huwa 1s ease-out forwards; } @keyframes huwa { to { opacity:1; transform:translateY(0%); } }

CSSの定番いきます。


こんにちは、「ふ」です。
今回はCSSアニメーションの定番、「スクロールに合わせて要素をふわっと浮かばせる」ものを作っていきたいと思います。
せっかくなので、「縦バージョン」「横バージョン」「複数要素」「時間差」など、いろんなケースに対応できるようにしましょう。

Js、Jqueryはちょっと....

「スクロールに合わせて」アニメーションさせるには、2022.2月現在のところJavaScriptによる制御が必須となります。

A : CSSだけで実装したかったのに、JavaScriptはちょっと....

B : Jqueryなどのライブラリは読み込みたくないのだが。

となった方も、ご安心ください。
JavaScriptに馴染みがなくてもコピペで実装できるよう、サンプルコードを用意しました。また、ネイティブのJavaScriptに含まれているInterSectionObserver APIを使用するので、外部ライブラリを読み込む必要はありません。

今回紹介する方法は外部へのアクセスもなく、ローカルファイル内で実装を完結させることができます。そのためページスピードにも悪影響なし。

一度作っておけば、あとは好きなときにすぐ利用することができます。
ぜひ挑戦してみてください。

クイック目次
目的のコードにジャンプします

IntersectionObserverについて詳しく知りたい方は、「理解編/実践編」の記事を用意しています⬇︎。参考にしてください。

IntersectionObserverを使いこなす(理解編)。

2022.01.30
「動いているだけのアニメーション」の先へと進もう。


IntersectionObserver、複数の要素や発生位置に対応する(実践編)。

2022.02.06
スクロールに応じたアニメーションを実践。


アニメーションの仕組み。


「ふわっと」の実装されている、いくつかのサイトを観察してみました。
要素を「ふわっと」浮かばせるためのアニメーション自体は、「移動」と「不透明度の変化」で作られているようです。

たとえば、最終的な要素の定位置がこちら(黄色の場所)だとしたら....

アニメーション前は要素を定位置からずらし、不透明度を下げておきます(「0」にしている場合も多かった)。

アニメーション時に要素は定位置へと戻る動きをし、不透明度も1まで上げてしまいます。
これで「ふわっと浮かび上がる」を表現しています。

■ elm1.svg CSSでこの動きを作ってみましょう。

原型のアニメーション。


<img id = "elm1" src = "elm1.svg">

#elm1 { 「「3/* はじめの状態 */」」 transform:translateY(50%); opacity:0; animation:huwa 1.5s alternate ease-out infinite; } 「「3/* アニメーションで定位置に戻す */」」 @keyframes huwa { to { transform:「「1initial」」; opacity:1; } }

アニメーションの終了状態を「translateY(0%)」ではなく「initial」としています。
こうしておくことで、規定の要素のずらし方向が水平の場合でも、このキーフレームを使い回すことができます。

結果です。これが「ふわっと」の原型。

現在は往復・繰り返しさせていますが、ユーザがスクロールして要素が画面に表示されたときに、このアニメーションを発生させます。
JavaScriptを使って、その制御を実装しましょう。

基本セット。


それではJavaScriptを使って、スクロールに対する制御を実装していきましょう。
その前に、画像とアニメーションのCSSを分離しておきます。

CSSを分離しておく。


#elm1 { transform:translateY(50%); opacity:0; } .huwa { animation:huwa 1.5s ease-out 「「1forwards」」; } @keyframes huwa { to { transform:initial; opacity:1; } }

先ほどのサンプルでは要素「#elm1」に直接アニメーションを指定してました。今後は別途でclass「huwa」を作成し、その中にアニメーションのCSSを配置しておきます。
アニメーションのタイミングについてですが、先ほどのinfinite(永遠に繰り返し)とalternate(交互に繰り返す)を、1回のみ再生(初期値)とforwards(終了時の状態を保つ)に変更しましょう。

アニメーションを発火させるタイミングで、要素に対して「.huwa」を追加するようにします。
そのタイミングは「画面と交差したとき」。
JavaScriptのIntersectionObserverを使って実装します。

単一要素(縦ver)


もっともよく見かける、「スクロールすると下からフワッと浮き上がる」ものを作ります。

「「3//① 要素を取得」」 const elm1 = document.getElementById("elm1"); 「「3//② 要素の50%が画面に交差したら」」 let options1 = {threshold:0.5}; 「「3//③ インスタンス生成」」 const observer1 = new IntersectionObserver(callback1,options1); 「「3//④ 監視対象を指定」」 observer1.observe(elm1); 「「3//⑤ コールバック関数」」 function callback1(entries) { 「「3//⑥ 画面と交差してるなら」」 if(entries[0].isIntersecting) { 「「3//⑦ 要素にclassを追加」」 elm1.classList.add("huwa"); } }


① 要素を取得。

② options1オブジェクトでは、交差の判断基準に関する指定を行います。ここでは「要素の0.5(50%)が画面と交差したとき、『交差した』ことになる」よう、指定しました。

③ IntersectionObserverのインスタンスを生成します。

④ いま生成したインスタンスの監視対象を要素に指定。

⑤ 交差判定が下されたときに呼び出される関数です。

⑥ IntersectionObserverは、ページ自体をロードしたときにも「交差が外れた」ということでフラグを立ててしまいます。そうならないよう、「交差しているなら」という条件を付けておきましょう。

⑦ 要素にアニメーションのclassを追加します。

結果⬆︎です。
枠内をスクロールすると、要素が交差したときに「フワッと」のアニメーションが発動します。

■ もう1度見るときは、左上の🔁ボタンをクリックしてください。

コピペコード⬇︎。


「「3<!-- ここ⬇︎に要素を入れて、class名を"elm1"としてください -->」」 <img id = "elm1" src = "elm1.svg"> <style> #elm1 { transform:translateY(50%); opacity:0; } .huwa { animation:huwa 1.5s ease-out forwards; } @keyframes huwa { to { transform:initial; opacity:1; } } </style> <script> const elm1 = document.getElementById("elm1"); let options1 = {threshold:0.5}; const observer1 = new IntersectionObserver(callback1,options1); observer1.observe(elm1); function callback1(entries) { if(entries[0].isIntersecting) { elm1.classList.add("huwa"); } } </script>

コピペ用のコードです。
HTMLの<body> 〜 </body>内にそのまま流し込んで使うことができます。要素は好きなものを入れて、class名を「elm1」としてください。

単一要素(横ver)

「横方向にフワッと」させているサイトもあったので、真似して作ってみました。
先ほどの縦verで、2箇所だけ変更を加えればOKです。

#elm1 { transform:「「1translateX(50%);」」 opacity:0; }

CSSでの要素の規定値。先ほどは「Y方向に50%」としていましたが、今回は「X方向に50%」とします。
アニメーションのキーフレームは「transform:initial」としていたので、規定値のずらし方向に関わらず、定位置に戻ってくる動きをしてくれます。

「「3//② 要素の100%が画面に交差したら、にする。」」 let options1 = { 「「1threshold:1」」 };

もう1つの変更はJavaScriptのoptions1オブジェクト。
thresholdプロパティで、「要素の0.5(50%)が交差したら発動」にしていました。しかしこのままでは要素が「斜め下からフワッと」浮かんでしまいます。
なるべく真横にスライドさせたいので、「1(100%)が交差したら発動」に変更します。

この⬆︎ようになりました。
スクロールさせると、要素が右側から入ってきます。
複数の要素を左右にずらしておいたりすると、雰囲気あるものが作れそうですね。

コピペコード⬇︎。


「「3<!-- ここ⬇︎に要素を入れて、class名を"elm1"としてください -->」」 <img id = "elm1" src = "elm1.svg"> <style> #elm1 { transform:translateX(50%); opacity:0; } .huwa { animation:huwa 1.5s ease-out forwards; } @keyframes huwa { to { transform:initial; opacity:1; } } </style> <script> const elm1 = document.getElementById("elm1"); let options1 = {threshold:1}; const observer1 = new IntersectionObserver(callback1,options1); observer1.observe(elm1); function callback1(entries) { if(entries[0].isIntersecting) { elm1.classList.add("huwa"); } } </script>

複数をふわり。


こんどは複数の要素にclass名を付けて、一括で「ふわり」を仕込んでみましょう。

複数要素(縦並び)


<img class = "elm2" src ="elm2.svg"> <img class = "elm2" src ="elm2.svg"> <img class = "elm2" src ="elm2.svg">

■ elm2.svg X3 縦方向に並んだ要素が、順にフワッと浮かんでくるものを作ってみましょう。

.elm2 { transform:translateY(「「1200%」」); opacity:0; }

CSS。
動きを大きくしないと感じが出なかったので、規定のずらしをY方向に「200%」としました。



「「3//① class要素すべてを取得」」 const elm2 = document.querySelectorAll(".elm2"); 「「3//② optionはデフォルト」」 let options2 = { }; const observer2 = new IntersectionObserver(callback2,options2); 「「3//③ 要素すべてを監視対象に」」 elm2.forEach(function(value) { bbobserver2.observe(value); bb}); function callback2(entries) { 「「3//④ 複数同時の交差にも対応」」 entries.forEach(function(value) { if(value.isIntersecting) { value.target.classList.add("huwa"); } }); }


① querySelectorAll( )を使って、指定したclassの要素をすべて取得します。
NodeListが返されるので、配列のように扱うことができます。

② 今回optionオブジェクトはデフォルトのままでいきました。調整が必要となったときのために、いちおー宣言しています。

③ forEach( )メソッドで、取得したすべての要素を監視対象に指定します。

④ 監視対象が複数なので、同時にobserverが発動する可能性があります。
そのときのためにclassの付与もforEach( )でおこなっています。

ゆっくりと下までスクロールしていくと、3つの要素が徐々に浮かんできます。
項目やボタンに使うと良さそう。 動きが派手過ぎず、なおかつユーザの目に留まるという効果が得られます。

コピペコード⬇︎。


「「3<!-- ここ⬇︎に複数の要素を入れて、class名を"elm2"としてください -->」」 <img class = "elm2" src = "elm2.svg"> <img class = "elm2" src = "elm2.svg"> <img class = "elm2" src = "elm2.svg"> <style> .elm2 { 「「3/*要素間の余白はお好みで。*/」」 margin-bottom:5%; transform:translateY(200%); opacity:0; } .huwa { animation:huwa 1.5s ease-out forwards; } @keyframes huwa { to { transform:initial; opacity:1; } } </style> <script> const elm2 = document.querySelectorAll(".elm2"); let options2 = { }; const observer2 = new IntersectionObserver(callback2,options2); elm2.forEach(function(value) { bbobserver2.observe(value); bb}); function callback2(entries) { entries.forEach(function(value) { if(value.isIntersecting) { value.target.classList.add("huwa"); } }); } </script>

複数要素(横並び)


<div class = "horizon"> <img class = "elm3" src = "elm3.svg"> <img class = "elm3" src = "elm3.svg"> </div>

.horizon { display:flex; } .elm3 { width:40%; padding:5%; }

先ほど実装したコードは、横並びの要素に対してもそのまま使えます。
ただしそれっぽくなるように、要素の規定値やoptionオブジェクトに手を加えました。

.elm3 { width:40%; padding:5%; 「「3/* 追加 */」」 transform:translateY(「「120%」」); opacity:0; }

要素規定値のずらしは小さめに、「下へ20%」としました。



let options3 = { bb「「1threshold:0.5」」 bb};

optionsオブジェクトで、「要素の0.5(50%)が重なったら発動させる」と指定。

複数の要素を扱う場合をやってみました。
その大きさや配置によっては、optionsプロパティやCSSの規定値を調整する必要が出てきます。みなさんも実装の際はいい感じになるように試行錯誤してみてください。

コピペコード⬇︎。


「「3<!-- ここ⬇︎に複数の要素を入れて、class名を"elm3"、親要素はclass名"horizon"としてください -->」」 <div class = "horizon"> <img class = "elm3" src = "elm3.svg"> <img class = "elm3" src = "elm3.svg"> </div> <style> .horizon { display:flex; } .elm3 { 「「3/* レイアウトは貼り付けた要素に合わせて調整してください。*/」」 width:40%; padding:5%; transform:translateY(20%); opacity:0; } .huwa { animation:huwa 1.5s ease-out forwards; } @keyframes huwa { to { transform:initial; opacity:1; } } </style> <script> const elm3 = document.querySelectorAll(".elm3"); let options3 = {threshold:0.5}; const observer3 = new IntersectionObserver(callback3,options3); elm3.forEach(function(value) { bbobserver3.observe(value); bb}); function callback3(entries) { entries.forEach(function(value) { if(value.isIntersecting) { value.target.classList.add("huwa"); } }); } </script>

時間差でふわっと。


先ほどの複数の横並び要素。ここまできたら(←?)、時間差で徐々に浮かんでくるようにしたいものです。
やってみましょう!

<div class = "timelag"> <img class = "lagchild" src = "elm4.svg"> <img class = "lagchild" src = "elm4.svg"> <img class = "lagchild" src = "elm4.svg"> </div>

.timelag { display:flex; } .timelag img { width:30%; margin:2%; }

3つの横並び要素を準備しました。
時間差をつけて「ふわっと」させるには、各要素にanimation-delayの値を少しづつ増やしながら与えていきます。

横並び要素を時間差で。



「「3//① 親要素が複数の場合にも対応させておく」」 const timelag = document.querySelectorAll(".timelag"); 「「3//② ターゲット要素をすべて取得」」 const lagchild = document.querySelectorAll(".lagchild"); 「「3//③すべての親要素に対して」」 for(i = 0; i<timelag.length; i++) { 「「3//④ 子要素を取得」」 let children = timelag[i].children; 「「3//⑤ 均等にdelayを与える」」 for (j = 0;j<children.length;j++) { children[j].style.animationDelay = 「「5String(j*(1/children.length))+"s"」」; } } 「「3//⑥ あとは複数verと同じ」」 let options5 = { }; const observer5 = new IntersectionObserver(callback5,options5); lagchild.forEach(function(value) { observer5.observe(value); }); function callback5(entries) { entries.forEach(function(value) { bbif(value.isIntersecting) { value.target.classList.add("huwa"); } }) }


① 「時間差のセット」を複数配置しているときのために、親要素の取得もquerySelectorAll( )を使いました。

③ ①で取得したすべての親要素に対して処理をおこないます。

④ 子要素を取得。
HTMLCollectionという、配列っぽくなった子要素が返ります。

⑤ 返されたHTMLCollectionの各要素に対して、均等にanimation-delayを与えます。ここでは「1を子要素の数で割った」ものにしました。

結果⬆︎です。
「お上品な感じ」が演出できましたね!

コピペコード⬇︎。


「「3<!-- ここ⬇︎に複数の要素を入れて、class名を"lagchild"、親要素はclass名"timelag"としてください -->」」 <div class = "timelag"> <img class = "lagchild" src = "elm4.svg"> <img class = "lagchild" src = "elm4.svg"> <img class = "lagchild" src = "elm4.svg"> </div> <style> .timelag { display:flex; width:90%; margin:0 auto; } .timelag img { 「「3/* レイアウトは貼り付けた要素に合わせて調整してください。*/」」 display:block; width:30%; padding:initial; margin:2%; opacity:0; transform:translateY(50%); } .huwa { animation:huwa 1.5s ease-out forwards; } @keyframes huwa { to { transform:initial; opacity:1; } } </style> <script> const timelag = document.querySelectorAll(".timelag"); const lagchild = document.querySelectorAll(".lagchild"); for(i = 0; i<timelag.length; i++) { let children = timelag[i].children; for (j = 0;j<children.length;j++) { children[j].style.animationDelay = 「「5String(j*(1/children.length))+"s"」」; } } let options5 = { }; const observer5 = new IntersectionObserver(callback5,options5); lagchild.forEach(function(value) { observer5.observe(value); }); function callback5(entries) { entries.forEach(function(value) { bbif(value.isIntersecting) { value.target.classList.add("huwa"); } }) } </script>

フルカバーのコード。


最後までお読みくださり、ありがとうございます。
「要素をフワッと浮かばせる」、色々なパターンを紹介してきました。特に最後の時間差verのコードについては、全てのパターンをカバーしているものです。コピペしてお使いください。

いつもながら記事が長くなってしまったので、あとがきは短めに(笑)
ではまた〜 ♪



SVGでWeb Animations API。

2020.11.12
〽️ ネイティブJavaScriptでのアニメーション。












「ふ」です。

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