SAMPLE
スクロールアニメーション(Intersection Observer)
INDEX
説明
IntersectionObserver API(交差監視 API)を使い、ビューポートに要素が入ってきたかどうかを判別します。
使用するライブラリ
なし
手順
手順1
下記のような aria-hidden="true"
となっている section
が、ビューポート(ブラウザ画面内)に入ってきたらaria-hidden="false"
に切り替わって表示されるようにしていきます。
<section class="section" aria-hidden="true">
<h2>Heading</h2>
<p>Lorem ipsum...</p>
</section>
<section class="section" aria-hidden="true">
<h2>Heading</h2>
<p>Lorem ipsum...</p>
</section>
...
手順2
CSSで.section
の transition
とaria-hidden
での各状態におけるスタイルを指定します。
.section {
& {
padding: 1rem;
transition: opacity 1s ease, visibility 1s ease;
}
&[aria-hidden="true"] {
opacity: 0;
visibility: hidden;
}
&[aria-hidden="false"] {
opacity: 1;
visibility: visible;
}
}
手順3
IntersectionObserver
を.section
に対して設定していきます。
基本的にはconst observer = new IntersectionObserver(callback, options)
で、交差時に実行されるcallback
とoptions
を渡してオブザーバーを作成したら、observe()
で要素を指定します。
options
の中身は下記の通り。
root
:基準となる要素を指定します。デフォルトはnull
で、最上位のビューポート=ブラウザの領域となります。rootMargin
:デフォルトは'0px'
。root
からのオフセットを指定します。cssのmargin
の様に'0px 0px 0px 0px'
や'100px 0px'
のような形で記載します。0の場合もpxや%の単位を指定しないとエラーになるので注意してください。root
がnull
のとき'-50% 0'
を指定すると、画面の真ん中が基準になります。threshold
:callback
が実行されるタイミングを指定します。0〜1の範囲で設定し、0は要素が入ってきたとき、1が要素が100%表示されたときです。[0, 0.5, 1]
だったら要素が入ったとき、半分表示された時、全部表示されたときにcallback
が実行されます。デフォルトは0.0
。
callback
は IntersectionObserverEntry
オブジェクトのリストとオブザーバーを受けとります。
IntersectionObserverEntry
については下記の通り。
IntersectionObserverEntry.target
:交差する要素を返します。IntersectionObserverEntry.isIntersecting
:要素が少しでもビューポートに入っている場合true
、全く入っていない場合false
を返します。IntersectionObserverEntry.intersectionRatio
:要素がどの程度表示されているかを0〜1の範囲の数値で取得することができます。(threshold
と同じルールです)IntersectionObserverEntry.boundingClientRect
:ターゲット要素の矩形の境界を返します。(具体的にはtop
,right
,bottom
,left
,x
,y
,width
,height
が取得できます)IntersectionObserverEntry.intersectionRect
:ターゲットの表示領域を返します。boundingClientRect
と同様にtop
,right
,bottom
,left
,x
,y
,width
,height
が取得できますが、簡単に説明すると縦スクロールの場合は見えている領域に応じてheight
の数値が変わっていきます。IntersectionObserverEntry.rootBounds
はルートとなる要素の矩形を返します。IntersectionObserverEntry.time
はIntersectionObserverが作成された時間を基準として、交差が記録された時刻を返します(単位はミリ秒)
サンプル1では要素が入ったかどうかの確認のみなので options
は全てデフォルト、callback
でもIntersectionObserverEntry.isIntersecting
とIntersectionObserverEntry.target
のみを使用します。
※ 複数のsection要素に同じ指定をするためobserver は配列に格納しています
// IntersectionObserver の threshold で実行されるアクション
// threshold は デフォルトで0.0 = 要素が画面内に入った時
const callback = (entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) { // 要素がビューポートに入ったとき
entry.target.setAttribute('aria-hidden', 'false')
} else { // 要素がビューポートから外れたとき
entry.target.setAttribute('aria-hidden', 'true')
}
})
}
// observer が複数なので、格納するための配列を作成
const observers = [];
// 対象としたい要素を指定
const sections = document.querySelectorAll('.section')
// IntersectionObserver を sectionsに適用
sections.forEach((e, i) => {
observers[i] = new IntersectionObserver(callback) // IntersectionObserber に callbackを渡してobserversのi番目の配列に格納
observers[i].observe(e) // 要素(e) を IntersectionObserver によって監視される対象要素に追加
})
サンプル1
上記の結果がこちら。<section>
のaria-hidden
を切り替える代わりに <img>
タグのsrc
を置き換えるような形にすると、loading="lazy"
が実装されていないブラウザ向けのlazyloadを作成することもできます。
サンプル2
こちらはthreshold
を[0.00, 0.01, 0.02,... 0.99, 1.00]
の100段階にし、IntersectionObserverEntry.intersectionRatio
の値でアニメーションさせたサンプルになります。
※わかりやすい様に intersectionRatioの値を%に変換して表示しています。
A:透明度を変更(opacityを0→1)
B:ぼかしを変更(blurを10px→0)※コンテンツ高さ>ビューポート高さだとぼかしがかかったままになります
C:背景色を変更(hslのh:色相の値を 0→360)
D:見出しを回転(h2 > spanのrotateの値を0deg→720deg)
E:見出しを移動(h2 > spanのtranslateXを0→200%)