スクロールに連動するアニメーションを実装する時は、JSのIntersection Observerを使用すると簡単に実装できる
Post on:2021年7月13日
スクロールに連動するアニメーションはIntersection Observerを使用すると、簡単に実装できます。カードUIがスクロールしてビューポートに表示されると、カード内の要素がアニメーションするUIを実装する方法を紹介します。
ライブラリなどは必要なく、数行のJavaScriptで簡単に実装できます。
Animate on scroll using Intersection Observer
by Keerthi
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
- はじめに
- Step 1: カードUIをHTMLとCSSで実装する
- Step 2: CSSに基本的なアニメーションを追加する
- Step 3: Intersection Observerを使用する前に、HTMLに変更を加える
- Step 4: JavaScriptのIntersection Observerを追加する
- Step 5: Intersection Observerのコールバック関数
- 完成
はじめに
ポートフォリオサイトなどにアニメーションを使用することは、ビジターの目を引き付け、サイトに長く滞在してもらうための素晴らしい方法です。
この記事では、ビジターが下や上にスクロールしたときに動作する特別なタイプのアニメーションをサイトに追加する方法を解説します。スクロールに連動するアニメーションの実装にはIntersection Observerという機能を使用します。
スクロールしたときに動作するアニメーション
YouTubeでも解説しています!
Step 1: カードUIをHTMLとCSSで実装する
最初に、基本的なカードUIのHTMLマークアップとスタイリングをレイアウトしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel='stylesheet' type='text/css' href='./style.css'> </head> <body> <div class="wrapper"> <div class="card"> <div class="image"></div> <h2>Profile picture</h2> <p>Some text goes here. Some text goes here.Some text goes here.Some text goes here....</p> </div> </div> </body> <html> |
外部CSSのstyle.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 |
.wrapper { min-height: 100vh; display: flex; align-items: center; justify-content: space-around; background-color: #5dafb8; color:white; font-family: Verdana, Geneva, Tahoma, sans-serif; } .card { height: 50vh; border-radius: 13px; box-shadow: 20px 40px 33px rgba(0,0,0,0.3); padding: 2rem; width: 35vh; background-color: #6cc2ce; } /* 背景画像としてグラデーションを使用してdivのスタイルを設定 */ .image { width: 35vh; height: 20vh; background-image: linear-gradient(70deg, RoyalBlue , DarkTurquoise ); background-size: cover; background-position: center center; box-shadow: 10px 15px 15px 6px #3891b4; border-radius: 15px; } |
上記のCSSでは、background-imageをグラデーションに設定しました。
1 |
background-image: linear-gradient(70deg, RoyalBlue , DarkTurquoise ); |
ここまでの実装で、下記のように表示されます。
カードUIをHTMLとCSSで実装
Step 2: CSSに基本的なアニメーションを追加する
次に、キーフレームアニメーションを使用して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 |
.card h2 { /* タイトルのアニメーション、左から右にフェードイン */ animation: animTitle 2s infinite; } .card p { /* パラグラフのアニメーション、右から左にフェードイン */ animation: animContent 2s infinite; } @keyframes animTitle { from { /* アニメーションの開始値を定義 */ transform: translateX(-50px); opacity: 0; } to { /* アニメーションの終了値を定義 */ transform: translateX(0); opacity: 1; } } @keyframes animContent { from { /* アニメーションの開始値を定義 */ transform: translateX(50px); opacity: 0; } to { /* アニメーションの終了値を定義 */ transform: translateX(0); opacity: 1; } } |
上記のCSSには、2つのキーフレームアニメーションがあります。
- @keyframes animTitle {...}
- @keyframes animContent { ...}
キーフレームアニメーションは、CSSのセレクタで呼び出すことができます。
- .card h2 {animation: animTitle 2s infinite;}
- .card p{animation: animContent 2s infinite;}
上記のCSSでは、どちらも2秒間実行され、無限にループするように定義しています。これらのアニメーションは、要素の水平方向のx値に対する単純なトランジション/トランスレートです。
また、画像には伸縮アニメーションを追加します。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 |
.card .image { /* 画像のアニメーション */ animation: animImage 2s infinite; } @keyframes animImage { 0% { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } 30% { -webkit-transform: scale3d(1.25, 0.75, 1); transform: scale3d(1.25, 0.75, 1); } 40% { -webkit-transform: scale3d(0.75, 1.25, 1); transform: scale3d(0.75, 1.25, 1); } 50% { -webkit-transform: scale3d(1.15, 0.85, 1); transform: scale3d(1.15, 0.85, 1); } 65% { -webkit-transform: scale3d(0.95, 1.05, 1); transform: scale3d(0.95, 1.05, 1); } 75% { -webkit-transform: scale3d(1.05, 0.95, 1); transform: scale3d(1.05, 0.95, 1); } 100% { -webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1); } } |
このアニメーションは、Animistaというオンラインのアニメーションジェネレーターで作成しました。このジェネレーターでは、他のアニメーションを試すこともできます。使い方は簡単で、キーフレームのコードを生成してくれるので、それをアニメーションさせたいコンテナに適用するだけです(上記のように)。
ここまでの実装は、CodePenで確認できます。
See the Pen
scroll-animation-step2 by ui-code-tv (@ui-code-tv)
on CodePen.
ここまでは、CSSでアニメーションを制御していましたが、スクロールに連動するアニメーションを実装するにはJavaScriptを使用する必要があります。また、それに合わせてHTMLとCSSにもいくつかの変更が必要です。それが次の作業です。
Step 3: Intersection Observerを使用する前に、HTMLに変更を加える
JavaScriptでアニメーションを動作させる際にポイントとなるのが、アニメーションのキーフレーム関数名と適用すべきルールへのアクセスです。今回の実装では、下記のCSSルールで実現しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
.card h2 { /* タイトルをアニメーション、左から右にフェードイン */ animation: animTitle 2s infinite; } .card p { /* パラグラフをアニメーション、右から左にフェードイン */ animation: animContent 2s infinite; } .card .image { /* 画像をアニメーション */ animation: animImage 2s infinite; } |
これをjavaScriptで動的に適用するには、これらのCSSを破棄し、HTMLのdata属性を使用して上記のアニメーション値を保存する必要があります。また、アニメーションさせる3つの要素には、.animateというクラスを付けます。したがって、カードのHTMLを下記のように変更します。
1 2 3 4 5 6 7 8 |
<div class="wrapper"> <div class="card"> <div class="image animate" data-animate="animImage 2s"></div> <h2 class="animate" data-animate="animTitle 2s ">Profile picture</h2> <p class="animate" data-animate="animContent 2s ">Some text goes here.Some text goes here....</p> </div> </div> |
ここで重要なのはdata属性で、例えば、画像コンテナのdata属性は、data-animate="animImage 2s"となります。
ここでは、data-animateというdataアイテムを作成し、その値をcssで定義したアニメーションに設定しています。ちょっと不思議な感じがしますが、data属性の使い方についてはMozilla.orgをご覧ください。
また、スクロールしてアニメーションを起動できるように、さらにコンテンツを追加する必要があるので、カードのラッパーをさらに3つ複製します。
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 |
<div class="wrapper"> <div class="card"> <div class="image animate" data-animate="animImage 2s"></div> <h2 class="animate" data-animate="animTitle 2s ">Profile picture</h2> <p class="animate" data-animate="animContent 2s ">Some text goes here.Some text goes here....</p> </div> </div> <div class="wrapper"> <div class="card"> <div class="image animate" data-animate="animImage 2s "></div> <h2 class="animate" data-animate="animTitle 2s ">Profile picture</h2> <p class="animate" data-animate="animContent 2s ">Some text goes here....Some text goes here....Some text goes here....Some text goes here....</p> </div> </div> <div class="wrapper"> <div class="card"> <div class="image animate" data-animate="animImage 2s "></div> <h2 class="animate" data-animate="animTitle 2s ">Profile picture</h2> <p class="animate" data-animate="animContent 2s ">Some text goes here....Some text goes here....Some text goes here....Some text goes here....</p> </div> </div> <div class="wrapper"> <div class="card"> <div class="image animate" data-animate="animImage 2s "></div> <h2 class="animate" data-animate="animTitle 2s ">Profile picture</h2> <p class="animate" data-animate="animContent 2s ">Some text goes here....Some text goes here....Some text goes here....Some text goes here....</p> </div> </div> |
Step 4: JavaScriptのIntersection Observerを追加する
Intersection Observerは基本的には、指示された要素を観察します。対象となる要素と祖先の要素との交点(Intersection)の変化を観察します。先祖要素はブラウザのビューポートとし、交差を観察する対象要素はカードの3つの要素、すなわちimage, p, h2タグです。
Intersection Observer APIについは、下記をご覧ください。
参考: IntersectionObserverを使用すると実装が簡単に!Vue.jsでスクロールイベントをトリガーする方法
通常はルート要素を定義しますが、今回のケースでは、ブラウザのビューポートをデフォルトにしたいので、ルート要素を定義していないことに注意してください(ルート要素を定義しない場合、祖先をルートとみなします)。というわけで、今回のユースケースの基本的なコード構造は下記のようになります。
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 |
<script> const callback = (entries) => { //4] Callback code goes here } //1]Create a new intersectionObserver object, //which will accept a callback function as //a parameter. let observer = new IntersectionObserver(callback); //2]Select all elements that have ".animate" //class.In our case we have three //elements (.image,<p> and h<2>). const animationItems = document.querySelectorAll('.animate'); //3]Loop through selected elements and add to the //observer watch list. animationItems.forEach(item => { observer.observe(item) }) </script> |
このコードをHTMLの中で、bodyタグ内の一番最後に追加します。簡単に説明すると、4つのステップがあります。
- Intersection Observerオブジェクトを作成する。
- 監視したいアイテムを照会して選択する。
- 選択したアイテムをIntersection Observerオブジェクトのウォッチリストに追加する。
- 交差イベントが発生するたびに何かを実行するコールバック関数を提供する。この場合、キーフレームアニメーションを添付する必要があります。
上記のコードでは、コールバック関数のコードを記述していません。これが次の作業となります。
Step 5: Intersection Observerのコールバック関数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
const callback = (entries) => { // The entries variable will contain the list of // elements that you are observing. When ever // intersection occurs, you need to do forEach loop // to find which one intersected. // For this we check a flag on the element called "isIntersecting" entries.forEach( (entry) => { if (entry.isIntersecting) { console.log("The element is intersecting >"); //If intersecting then attach keyframe animation. //We do this by assigning the data attribute //we coded in the markup to the style.animation //of the element entry.target.style.animation = entry.target.dataset.animate; } else { //We take of the animation if not in view entry.target.style.animation="none"; } } ); } |
このコールバックは、交差が発生するたびに呼び出されます。コールバックは、オブザーバーのリストにあるすべての要素にアクセスします。コールバックでは、どの要素が交差したかをループして調べます。そのためには、要素にあるisIntersectingというフラグをチェックします。if文を確認すると、これがわかります。
if (entry.isIntersecting) {...}
交差している場合は、キーフレームアニメーションを要素にアタッチし、次の行がそれを行います。
entry.target.style.animation = entry.target.dataset.animate
ここでは、要素のstyle.animationにStep 3で設定したdata-animateというdata属性を取得して設定します。例えば、画像の場合は、要素のマークアップのdata属性から文字列の部分を取得します。
<div class="image animate" data-animate="animImage 2s"></div>
この場合は、animImage 2sです。
コードのelseの部分は、アニメーションが交差していないため、アニメーションを削除します
1 |
entry.target.style.animation="none"; |
これで前後にスクロールすると、アニメーションが再び実行されます。
完成
実際の動作は、下記のCodePenでご覧ください。
See the Pen
scroll-animation-step3 by ui-code-tv (@ui-code-tv)
on CodePen.
sponsors