画面をスクロールして、要素が画面内に入ったらフワッと表示させるという、制作者側からしたら見飽きたけどクライアント側からは依然人気の高い例のアレ。
実は簡単に実装できるので、今まではその都度、状況に合わせてCSSとJSを書いていたのですが、
最近になって大体の要望や状況のパターンが掴めてきたので、汎用的なスクリプトを用意して毎回コピペで何とかできないものかと考えてみました。
色々試した所、今まで面倒くさがってjQueryで書いていたんですが、実は普通にJavaScriptで書いた方が簡単という事を知りました。
ですので、プレーンなJavaScript、CSS、HTMLのコードで紹介していきたいと思います。
HTML・CSS
基本のHTMLとCSSを用意
まず適当なHTMLとCSSを用意します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<body> <header> <h1>ヘッダー</h1> </header> <section> <div>要素1</div> <div>要素2</div> <div>要素3</div> <div>要素4</div> <div>要素5</div> <div>要素6</div> <div>要素7</div> <div>要素8</div> </section> <section> <div>要素9</div> <div>要素10</div> <div>要素11</div> <div>要素12</div> <div>要素13</div> <div>要素14</div> <div>要素15</div> <div>要素16</div> </section> </body> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
body { margin: 0; } header { background: #0cc; height: 700px; padding: 100px; box-sizing: border-box; } section { display: flex; flex-wrap: wrap; justify-content: space-between; padding: 5%; box-sizing: border-box; } section div { width: 23%; height: 300px; margin: 0 0 2.5vw; padding: 3%; box-sizing: border-box; background: #09c; } |
ここまでのHTMLとCSSは何でも良い(transformを使ってなければ)ので適当です。
今回の件で必要になるのは、ここからです。
アニメーション用のCSS
まず、アニメーションさせる要素にクラスを付けていきます。
今回、汎用的なものを作るという目的がありますので、どんなサイトでも他と被らないようなクラスを用意しておきたいです。
ここではとりあえず、scrollとanimationから頭文字を取って sa というクラスにする事にします。
このsaクラスを、フワッと出現させたい要素全てに追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<section> <div class="sa">要素1</div> <div class="sa">要素2</div> <div class="sa">要素3</div> <div class="sa">要素4</div> <div class="sa">要素5</div> <div class="sa">要素6</div> <div class="sa">要素7</div> <div class="sa">要素8</div> </section> <section> <div class="sa">要素9</div> <div class="sa">要素10</div> <div class="sa">要素11</div> <div class="sa">要素12</div> <div class="sa">要素13</div> <div class="sa">要素14</div> <div class="sa">要素15</div> <div class="sa">要素16</div> </section> |
そして、どのようにアニメーションさせるかを決めるクラスを同じ要素に追加します。
今回用意したクラスは下記の8種類です。
.sa–lr
左から右にフェードイン
.sa–rl
右から左にフェードイン
.sa–up
下から上にフェードイン
.sa–down
上から下にフェードイン
.sa–scaleUp
縮小した状態から拡大しながらフェードイン
.sa–scaleDown
拡大した状態から縮小しながらフェードイン
.sa–rotateL
左に回転しながらフェードイン
.sa–rotateR
右に回転しながらフェードイン
これを先程の要素にそれぞれ追加していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<section> <div class="sa sa--up">要素1</div> <div class="sa sa--up">要素2</div> <div class="sa sa--up">要素3</div> <div class="sa sa--up">要素4</div> <div class="sa sa--up">要素5</div> <div class="sa sa--up">要素6</div> <div class="sa sa--up">要素7</div> <div class="sa sa--up">要素8</div> </section> <section> <div class="sa sa--lr">要素9</div> <div class="sa sa--rl">要素10</div> <div class="sa sa--up">要素11</div> <div class="sa sa--down">要素12</div> <div class="sa sa--scaleUp">要素13</div> <div class="sa sa--scaleDown">要素14</div> <div class="sa sa--rotateL">要素15</div> <div class="sa sa--rotateR">要素16</div> </section> |
CSSを書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
.sa { opacity: 0; transition: all .5s ease; } .sa.show { opacity: 1; transform: none; } .sa--lr { transform: translate(-100px, 0); } .sa--rl { transform: translate(100px, 0); } .sa--up { transform: translate(0, 100px); } .sa--down { transform: translate(0, -100px); } .sa--scaleUp { transform: scale(.5); } .sa--scaleDown { transform: scale(1.5); } .sa--rotateL { transform: rotate(180deg); } .sa--rotateR { transform: rotate(-180deg); } |
それぞれのクラスで初期状態を設定しておき、.showが追加されたら、正常な状態に戻す、という事になります。
ここまでの状態のページがこちらです。→ サンプルページ
下に並んでいた青い四角の要素が、opacity:0の状態でスタンバイしています。
デベロッパーツールでshowというクラスを追加してみると、フワッと出現するのがわかると思います。
このshowクラスの追加をJavaScriptで行うわけですね。
JavaScript
基本的なコード
スクロールして、画面の下と要素の位置の差が○px(今回は300px)になったら、要素にshowクラスを追加する。というコードを書くわけですが、
とりあえず汎用性を無視して極力短く書いてみると、この程度のコードで済みます。
1 2 3 4 5 6 7 8 9 10 11 |
var scrollAnimationElm = document.querySelectorAll('.sa'); var scrollAnimationFunc = function() { for(var i = 0; i < scrollAnimationElm.length; i++) { var triggerMargin = 300; if (window.innerHeight > scrollAnimationElm[i].getBoundingClientRect().top + triggerMargin) { scrollAnimationElm[i].classList.add('show'); } } } window.addEventListener('load', scrollAnimationFunc); window.addEventListener('scroll', scrollAnimationFunc); |
このスクリプトをbodyの閉じタグの前で読み込みます。
スクロールすると青い四角の要素がフワッと出現するようになりました。
こんなに短い行数で書けるんですね。
何故今までわざわざjQueryで書いていたのか。アホらしくなります。
しかし、少し問題があります。
showクラスを追加するタイミングを、その要素がスタンバイしている位置(transfromが効いた状態)で判定しているため、
横一列に並んでいる要素でも出現するタイミングがずれてしまいます。
サンプルページだと、「要素12」の出現が早く、「要素11」の出現が遅いので、アニメーションがバラバラとしてしまいます。
という事で、1つ機能を追加します。
表示タイミングの基準の要素を指定できる機能を追加
表示するタイミングはその要素自身の位置を基準にしていましたが、その基準を特定の要素の位置に変更できるようにします。
例として、サンプルページの青い四角群の3列目は「要素9」を基準に、4列目は「要素15」を基準にしてみます。
まず、基準となる要素にIDを付けてみます。
1 2 3 4 5 6 7 8 9 10 |
<section> <div class="sa sa--lr" id="elm9">要素9</div> <div class="sa sa--rl">要素10</div> <div class="sa sa--up">要素11</div> <div class="sa sa--down">要素12</div> <div class="sa sa--scaleUp">要素13</div> <div class="sa sa--scaleDown">要素14</div> <div class="sa sa--rotateL" id="elm15">要素15</div> <div class="sa sa--rotateR">要素16</div> </section> |
要素9に”elm9″、要素15に”elm15″というIDを付けてみました。
そして、基準位置を変更したい要素にオリジナルのdata属性を追加していきます。
data-sa_triggerという属性を用意しました。
このdata-sa_trigger属性に、基準にしたい要素をそれぞれ指定していきます。
1 2 3 4 5 6 7 8 9 10 |
<section> <div class="sa sa--lr" id="elm9">要素9</div> <div class="sa sa--rl">要素10</div> <div class="sa sa--up" data-sa_trigger="#elm9">要素11</div> <div class="sa sa--down" data-sa_trigger="#elm9">要素12</div> <div class="sa sa--scaleUp" data-sa_trigger="#elm15">要素13</div> <div class="sa sa--scaleDown" data-sa_trigger="#elm15">要素14</div> <div class="sa sa--rotateL" id="elm15">要素15</div> <div class="sa sa--rotateR">要素16</div> </section> |
少し見にくくなりましたが、
3列目の要素11と要素12に
data-sa_trigger="#elm9" を、
4列目の要素13と要素14に
data-sa_trigger="#elm15" を、追加しています。
そしてJavaScriptです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var scrollAnimationElm = document.querySelectorAll('.sa'); var scrollAnimationFunc = function() { for(var i = 0; i < scrollAnimationElm.length; i++) { var triggerMargin = 300; var elm = scrollAnimationElm[i]; var showPos = 0; if(elm.dataset.sa_trigger) { showPos = document.querySelector(elm.dataset.sa_trigger).getBoundingClientRect().top + triggerMargin; } else { showPos = elm.getBoundingClientRect().top + triggerMargin; } if (window.innerHeight > showPos) { elm.classList.add('show'); } } } window.addEventListener('load', scrollAnimationFunc); window.addEventListener('scroll', scrollAnimationFunc); |
data-sa_trigger属性に値があれば指定の要素、無ければその要素自身の位置を判定基準にする、といった感じです。
見事に横一列の表示タイミングが揃いました。美しいですね。
しかし、綺麗に揃って出現するのは美しいですが、意図的にずらして表示させたいという場面も多々あります。
という事で、機能をもう1つ追加します。
表示のタイミングを位置でずらす機能を追加
JSのコードの中に、triggerMargin = 300; というものがありますが、
これは「その要素が画面の下から300px上に上がって来たら表示する」という意味になります。
これまでは全ての要素が一律300pxのタイミングで表示されていましたが、
これを要素毎に自由に変えられるようにします。
先程と同じく、オリジナルのdata属性を追加します。
今回は data-sa_margin という属性にします。
これに300以外の数値を指定して、表示するタイミングをずらせるようにします。
青い四角群の1列目と2列目の要素に追加してみます。
1 2 3 4 5 6 7 8 9 10 |
<section> <div class="sa sa--up" data-sa_margin="0">要素1</div> <div class="sa sa--up" data-sa_margin="100">要素2</div> <div class="sa sa--up" data-sa_margin="200">要素3</div> <div class="sa sa--up" data-sa_margin="300">要素4</div> <div class="sa sa--up" data-sa_margin="400">要素5</div> <div class="sa sa--up" data-sa_margin="500">要素6</div> <div class="sa sa--up" data-sa_margin="600">要素7</div> <div class="sa sa--up" data-sa_margin="700">要素8</div> </section> |
JavaScriptです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
var scrollAnimationElm = document.querySelectorAll('.sa'); var scrollAnimationFunc = function() { for(var i = 0; i < scrollAnimationElm.length; i++) { var triggerMargin = 300; var elm = scrollAnimationElm[i]; var showPos = 0; if(elm.dataset.sa_margin != null) { triggerMargin = parseInt(elm.dataset.sa_margin); } if(elm.dataset.sa_trigger) { showPos = document.querySelector(elm.dataset.sa_trigger).getBoundingClientRect().top + triggerMargin; } else { showPos = elm.getBoundingClientRect().top + triggerMargin; } if (window.innerHeight > showPos) { elm.classList.add('show'); } } } window.addEventListener('load', scrollAnimationFunc); window.addEventListener('scroll', scrollAnimationFunc); |
デフォルトは300px、もしdata-sa_marginに値があれば、その数値に置き換える。といった感じです。
それぞれ指定したタイミングで表示されるようになりました。
あまり見た目にインパクトのある機能ではありませんが、
例えば少し変わったデザインのサイトをコーディングする時、画像の上の方が透過になっていて、その要素の位置が見た目よりも意外と上にある、みたいな事もあります。
そういう時にこの機能があると表示タイミングを調節できるので、意外と役に立つ機能なのです。
逆に、見た目上美しくずらす場合は、位置をずらすよりも時間でずらした方が綺麗に見えます。
その機能を最後に追加したいと思います。
表示のタイミングを時間でずらす機能を追加
これもまたdata属性を使用して、表示タイミングを時間でずらせるようにします。
今回はdata-sa_delayです。ミリ秒で指定します。
1 2 3 4 5 6 7 8 9 10 |
<section> <div class="sa sa--up">要素1</div> <div class="sa sa--up" data-sa_delay="200">要素2</div> <div class="sa sa--up" data-sa_delay="400">要素3</div> <div class="sa sa--up" data-sa_delay="600">要素4</div> <div class="sa sa--up" data-sa_delay="600">要素5</div> <div class="sa sa--up" data-sa_delay="400">要素6</div> <div class="sa sa--up" data-sa_delay="200">要素7</div> <div class="sa sa--up">要素8</div> </section> |
JavaScriptです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var scrollAnimationElm = document.querySelectorAll('.sa'); var scrollAnimationFunc = function() { for(var i = 0; i < scrollAnimationElm.length; i++) { var triggerMargin = 300; var elm = scrollAnimationElm[i]; var showPos = 0; if(elm.dataset.sa_margin != null) { triggerMargin = parseInt(elm.dataset.sa_margin); } if(elm.dataset.sa_trigger) { showPos = document.querySelector(elm.dataset.sa_trigger).getBoundingClientRect().top + triggerMargin; } else { showPos = elm.getBoundingClientRect().top + triggerMargin; } if (window.innerHeight > showPos) { var delay = (elm.dataset.sa_delay)? elm.dataset.sa_delay : 0; setTimeout(function(index) { scrollAnimationElm[index].classList.add('show'); }.bind(null, i), delay); } } } window.addEventListener('load', scrollAnimationFunc); window.addEventListener('scroll', scrollAnimationFunc); |
data-sa_delay属性に値があればその時間、無ければ0秒で表示するといった感じです。
美しくずらす事ができました。
今回用意した機能は以上になります。
それぞれの機能を組み合わせる事もできますので、
例えばdata-sa_trigger属性とdata-sa_delay属性を組み合わせれば、指定のタイミングで一気にアニメーションを動かすというような演出もできます。
気持ちいいですね。
最終的なコード
今回は汎用的なものを作るのが目的ですので、もう少し整理します。
スクリプトの上部で、
・スクロールのアニメーションで使用するクラス名(今回は “sa”)
・表示時に追加するクラス名(今回は “show”)
・画面下から表示するまでの距離のデフォルト値
を、設定できるようにした最終的なコードがこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
var scrollAnimationClass = 'sa'; var scrollAnimationShowClass = 'show'; var triggerMarginDefault = 300; var scrollAnimationElm = document.querySelectorAll('.' + scrollAnimationClass); var scrollAnimationFunc = function() { var dataMargin = scrollAnimationClass + '_margin'; var dataTrigger = scrollAnimationClass + '_trigger'; var dataDelay = scrollAnimationClass + '_delay'; for(var i = 0; i < scrollAnimationElm.length; i++) { var triggerMargin = triggerMarginDefault; var elm = scrollAnimationElm[i]; var showPos = 0; if(elm.dataset[dataMargin] != null) { triggerMargin = parseInt(elm.dataset[dataMargin]); } if(elm.dataset[dataTrigger]) { showPos = document.querySelector(elm.dataset[dataTrigger]).getBoundingClientRect().top + triggerMargin; } else { showPos = elm.getBoundingClientRect().top + triggerMargin; } if (window.innerHeight > showPos) { var delay = (elm.dataset[dataDelay])? elm.dataset[dataDelay] : 0; setTimeout(function(index) { scrollAnimationElm[index].classList.add('show'); }.bind(null, i), delay); } } } window.addEventListener('load', scrollAnimationFunc); window.addEventListener('scroll', scrollAnimationFunc); |
data属性もクラス名と統一できるようにしているので、
1行目を、
var scrollAnimationClass = 'abc'; にすれば、
data属性もそれぞれ、
data-abc_trigger
data-abc_margin
data-abc_delay
という事になります。
以上です。
本当はアニメーションで動く距離やスピードもdata属性で指定できるようにしたかったんですが、
それをやろうと思うとJavaScriptでCSSを弄らなきゃいけなくなってしまい、それだけは避けたかったので諦めました。
アニメーションの調整や、種類を増やしたい場合はCSSの方で何とかする。という事で自分の中では落ち着いています。
他のサイトではできなかったのがこちらのサイトではできました!
ありがとうございます。
いつも使わせていただいておりコメント書かせていただきました。
シンプルですごく使いやすいです!
ありがとうございます!
シンプルでわかりやすいコードをありがとうございます!
要素をもう少しゆっくり表示したい場合は、どうしたら良いでしょうか?
var fadeSpeed = 1000; などを記入すればいいのでしょうか?
コメントありがとうございます。
記事の最後の行に書いた通り、アニメーション自体の調整はCSS側で行うという事になりますので、
CSSの↓の箇所の3行目の部分
.sa {
opacity: 0;
transition: all .5s ease; // ← この部分
}
を、変更すれば大丈夫です。
.5s は 0.5s の略で、0.5秒という意味なので、
1秒にしたい場合は、1s とか 1000ms とかにする感じです。
なるほどですね!ご返信ありがとうございました!やってみます^^
大変勉強になりました。
私は、趣味の俳句のHPを作成中(サーバにはまだアップしておりません)ですが、JavaScriptの「最終的なコード」を改変して私の趣味の俳句のHPに使用させていただけないでしょうか。何卒、使用のご許可頂きたく存じます。
コメントありがとうございます。
記事内で紹介しているソースコードは特にライセンスがあるわけではありませんので、いくらでも使用していただいて大丈夫です。
お役に立てれば幸いです。
お返事頂き、有難うございます。
ソースコード、使用させて頂きます。
この度は有難うございます。