
【JavaScript】スクロールしてコンテンツをふわっと(フェードイン)表示させる方法

Webサイトをスクロールしたらフワッとコンテンツが表示されることがありますが、どうやって実装したらいいかわからなくてお困りの方もいるのではないでしょうか?
この記事ではアニメーションの仕組みと実装方法について解説していますのでぜひ参考にして下さい。
この記事は以下のお悩みを解決します。
- ・スクロールするとコンテンツがフワッと表示するアニメーションの実装方法を知りたい
- ・負荷がかからない実装方法を知りたい
1. フェードインの仕組み
Webサイトをスクロールしたときにコンテンツがフワッと表示されるアニメーションは「フェードイン」と呼ばれ、ユーザーの注意を引きやすく、サイト全体の印象もおしゃれになります。
スクロールして要素がフェードインするとき仕組みは以下のようになります。まずCSSのopacity: 0で透明にし、transitionでアニメーションの時間を指定した要素を準備し、スクロールして要素が特定の位置に到達したらJavaScriptでクラスを追加します。
追加したクラスにはopacity:1を指定します。これによりtransition(transition-duration)で指定した時間で要素がゆっくり表示されます。

これをアニメーションさせる各要素で実行します。次に実装方法をみていきましょう。
2. スクロールイベントで実装
最初にスクロールイベントで実装する方法をご紹介します。アニメーションで表示する要素に「fadein」クラスを指定し、要素がブラウザの下から20%より上の位置に到達したら表示するようにします。
コードは以下のように実装します。CodePenでアニメーションの動きを確認してみてください。
See the Pen Scroll FadeIn by CODE SQUARE (@Coffee1610) on CodePen.
HTMLのコードは以下のとおりです。
<header class="header">
<h1>ヘッダー</h1>
</header>
<div class="mainvisual"></div>
<div class="container">
<h2 class="content-title fadein">コンテンツ</h2>
<div class="content fadein">
<img src="ここに表示したい画像のパスを入力してください" alt="">
<p>テキストテキストテキストテキストテキストテキストテキスト</p>
</div>
<div class="content fadein">
<img src="ここに表示したい画像のパスを入力してください" alt="">
<p>テキストテキストテキストテキストテキストテキストテキスト</p>
</div>
<div class="content fadein">
<img src="ここに表示したい画像のパスを入力してください" alt="">
<p>テキストテキストテキストテキストテキストテキストテキスト</p>
</div>
<div class="content fadein">
<img src="ここに表示したい画像のパスを入力してください" alt="">
<p>テキストテキストテキストテキストテキストテキストテキスト</p>
</div>
<div class="content fadein">
<img src="ここに表示したい画像のパスを入力してください" alt="">
<p>テキストテキストテキストテキストテキストテキストテキスト</p>
</div>
</div>
<footer class="footer">
<p>フッター</p>
</footer>
CSSのコードは以下のとおりです。
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
.header {
width: 100%;
height: 70px;
background-color: rgba(255, 255, 255, 0.5);
position: absolute;
top: 0;
left: 0;
text-align: center;
}
.header h1{
line-height: 70px;
}
.mainvisual {
background-image: url("ここに表示したい画像のパスを入力してください");
background-position: center;
background-repeat: no-repeat;
background-size: cover;
height: 100vh;
}
.container {
max-width: 1200px;
padding: 0 30px;
margin: 0 auto;
margin-top: 120px;
}
.content-title {
margin-bottom: 90px;
text-align: center;
transition: 0.5s;
transform: translateY(-20px);
opacity: 0;
}
.content-title.show {
transform: translateY(0);
opacity: 1;
}
.content {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 60px;
opacity: 0;
transition: 0.5s;
visibility: hidden;
}
.content:nth-of-type(odd) {
transform: translateX(-20px);
}
.content:nth-of-type(even) {
flex-direction: row-reverse;
transform: translateX(20px);
}
.content:nth-of-type(odd).show,
.content:nth-of-type(even).show
{
transform: translateX(0);
opacity: 1;
visibility: visible;
}
.content img{
width: 25%;
aspect-ratio: 1/1;
object-fit: cover;
}
.content p {
width: 70%;
}
.footer {
background-color: #DDD;
padding: 20px;
text-align: center;
}
CSSの解説
.content-title {
margin-bottom: 90px;
text-align: center;
transition: 0.5s;
opacity: 0;
transform: translateY(-20px);
}
39行目:transition: 0.5s;
40行目:opacity: 0;
41行目:transform: translateY(-20px);
h2タグ「content-title」を透明にし、上に20px移動しています。transform: translateを指定することで動きながら要素がフェードインします。
.content-title.show {
transform: translateY(0);
opacity: 1;
}
45行目:transform: translateY(0);
46行目:opacity: 1;
JavaScriptで「show」クラスが追加されると下に移動しながら要素がフェードインします。
.content {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 60px;
opacity: 0;
transition: 0.5s;
}
.content:nth-of-type(odd) {
transform: translateX(-20px);
}
.content:nth-of-type(even) {
flex-direction: row-reverse;
transform: translateX(20px);
}
.content:nth-of-type(odd).show,
.content:nth-of-type(even).show
{
transform: translateX(0);
opacity: 1;
}
59行目:transform: translateX(-20px);
divタグ「content」の奇数番目の要素を左へ20px移動します。「show」クラスが追加されると右に動きながらフェードインします。
61行目:transform: translateX(20px);
divタグ「content」の偶数番目の要素を右へ20px移動します。「show」クラスが追加されると左に動きながらフェードインします。
JavaScriptのコードは以下のとおりです。
//要素をフェードインする処理
const showElements = () => {
//フェードインする要素を全て取得
const elements = document.querySelectorAll(".fadein");
//ブラウザの高さの80%を計算
const displayPos = window.innerHeight * 0.8;
elements.forEach((element) => {
//ブラウザの上から要素の上までの距離
const elementPos = element.getBoundingClientRect().top;
//要素がブラウザの下から20%の位置よりも上に到達したら実行する
if(displayPos > elementPos) {
//要素に「show」クラスを追加
element.classList.add("show");
}
});
};
window.addEventListener("load", showElements);
window.addEventListener("scroll", showElements);
JavaScriptの解説
4行目:const elements = document.querySelectorAll(“.fadein”);
フェードインさせる要素を全て取得します。
- querySelectorAll() :指定されたセレクターに一致する全ての要素を取得し、NodeListで返す。NodeListは配列のようにインデックスを用いて要素にアクセスできる。
6行目:const displayPos = window.innerHeight * 0.8;
「window.innerHeight * 0.8」はブラウザの表示領域の高さの80%になります。「0.8」を変えると要素のアニメーションを実行する位置を調整できます。
- window.innerHeight :ブラウザの表示領域の高さ(スクロールバーも含む)を取得する
8-16行目:elements.forEach((content) => {~});
取得した各要素に対して関数を実行します。
- NodeList.forEach(callback) :NodoList(または配列)の各要素に対して関数を実行する。
- ・callback :NodeList(または配列)の各要素に対して実行する関数
10行目:const elementPos = element.getBoundingClientRect().top;
ブラウザの上から要素の上までの距離を取得しています。
- element.getBoundingClientRect().top :ブラウザの上から指定した要素の上までの距離を取得する

12-15行目:if(displayPos > elementPos) {~}
下にスクロールするにつれelementPosの値は小さくなります。elementPosがブラウザの高さの80%より小さくなったら処理を実行します。
これは要素がブラウザの下から20%の位置より上に到達したらフェードインすると言い換えることができます。
13行目: element.classList.add(“show”);
要素のクラスに「show」を追加します。
- element.classList.add(“class”):要素に指定したクラスを追加する。
19行目:window.addEventListener(“load”, showElements);
20行目:window.addEventListener(“scroll”, showElements);
関数showElementsを登録しています。「load」はページが読み込まれたとき、「scroll」はスクロールしたときにshowElementsが実行されます。
- element.addEventListener(type, listener) :windowオブジェクトや要素に特定のイベントが発生したときに実行する関数を登録する。
- ・type: イベントの種類を指定する。例:click、load、scrollなど
- ・listener: イベントが発生後に実行される関数を指定する。
3. IntersectionObserverで実装
次にIntersectionObserverを使ったスクロールアニメーションの実装方法をご紹介します。
スクロールイベントで実装するとスクロールするたびに毎回発火するので、どうしても処理が重くなります。その結果カクつきの原因になります。
パフォーマンスも考慮したい場合はIntersectionObserverで実装するのがおすすめです。まずIntersectionObserverがどのようなものかをみていきましょう。
3-1. IntersectionObserverとは?
IntersectionObserverは特定の要素と基準要素の2つの要素が交差したかどうかを監視できるJavaScriptのAPIです。スクロールイベントのように毎回関数を実行するのではなく、要素が交差したタイミングだけで処理を行えるため、パフォーマンス面でも非常に優れています。
IntersectionObserverの記述方法は以下のとおりです。IntersectionObserverの第1引数は実行する関数、第2引数はオプションです。
const options = {
root: document.querySelector(".root"),
rootMargin: "10px"
threshold: 0.5
}
const observer = new IntersectionObserver(callback, options);
//監視を開始 監視対象の要素(target)を引数に指定する
observer.observe(target);
オプションは任意で指定でき、交差の判定条件を細かく調整できます。オプションで設定できる項目は以下の3つになります。
- root
- 交差を判定する基準となる要素です。監視対象の要素がrootの範囲内に入ったかどうかで、関数を実行します。nullまたは指定しない場合は、ビューポート(ブラウザの表示領域)が基準となります。
- rootMargin
- root周りのマージンで交差判定エリアを拡大、縮小できます。”10px”を指定するとrootの周囲10px分のマージンが追加され交差判定エリアが広がります。また、CSSのmarginのように”上 右 下 左”の順で個別に指定もできます。単位「px」「%」の指定は必須です。
- threshold
- 監視対象の要素がrootとどの程度交差したら関数を実行するかを指定します。指定範囲は0~1。例えば0なら監視対象の要素が見えた時点で、1なら全ての部分が交差したら関数を実行します。
ではスクロールイベントとIntersectionObserverの実行回数がどのくらい変わるかを比較してみます。それぞれの方法でconsole.logを実行します。IntersectionObserverは監視対象の要素を設置してブラウザに表示されたら実行するように指定します。
window.addEventListener('scroll', () => {
console.log('scroll');
});
//監視対象の要素を取得
const box = document.querySelector('.box');
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if(entry.isIntersecting) {
console.log('IntersectionObserver');
}
});
});
//監視を開始
observer.observe(box)
以下は上記のコードを実行した動画です。赤い四角形が監視対象の要素「box」です。コンソールに注目してみて下さい。
「scroll」はスクロールするごとに何度も実行されているのに対して、「IntersectionObserver」は監視対象の要素が表示された一度だけ実行されています。
IntersectionObserverを使うと負荷が抑えられるということがわかりますね。
3-2. 実装方法
スクロールイベントで実装したアニメーションをIntersectionObserverで実装します。HTMLとCSSのコードはスクロールイベントで実装したときと同じです。CodePenでアニメーションを確認してみてください。
See the Pen Scroll FadeIn2 by CODE SQUARE (@Coffee1610) on CodePen.
JavaScriptのコードは以下のとおりです。
//監視対象の要素を全て取得する
const elements = document.querySelectorAll(".fadein");
const options = {
//監視対象の要素が50%基準要素と交差したら実行する
threshold: 0.5,
}
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
//監視対象の要素が基準要素と交差したら実行する
if (entry.isIntersecting) {
entry.target.classList.add("show");
}
});
}, options);
elements.forEach((element) => {
//要素の監視を開始する
observer.observe(element);
});
JavaScriptの解説
4-7行目:const options = {~}
オプションを指定しています。rootは指定していないのでビューポートが基準要素になります。
6行目: threshold: 0.5,
監視対象の要素が50%ビューポートと交差したら処理を実行します。
12-14行目:if (entry.isIntersecting) {~}
「entry.isIntersecting」は監視対象の要素の50%ビューポートと交差したら、trueを返します。
13行目:entry.target.classList.add(“show”);
要素に「show」クラスを追加します。「entry.target」は監視している要素を指します。
20行目:observer.observe(element);
要素の監視を開始します。elementを引数に渡して、それぞれの「fadein」クラスの要素に対して監視を開始します。
4. まとめ
今回はスクロールしたら要素をふわっと表示する方法をご紹介しました。
小規模なサイトであればスクロールイベントでも十分かもしれませんが、より多くの要素をアニメーションさせたり、複雑な処理を行うサイトでは、パフォーマンスの低下を抑えられるIntersectionObserverでの実装がおすすめです。最終的にはIntersectionObserverを使いこなせるように、ぜひご自身で実装を試してみてください。
最後まで読んでいただきありがとうございました。