スクロールして要素が画面に入ったときに、横からスーッとスライドしながらフェードインさせる演出は、ページに動きと注目を与えます。
この記事では、CSSの transform + transition と、現代的な IntersectionObserver を組み合わせて、画像や文字を横(左右)からフェードインさせる方法を、アクセシビリティ対応まで含めて解説します。opacityだけの「ふわっと表示」はIntersectionObserverでふわっと表示させる方法を参照してください。
opacity: 0 + transform: translateX(...)(横にずらす)にし、IntersectionObserver で画面に入ったらクラスを付与して元の位置に戻します。方向は translateX / translateY で変えられます。prefers-reduced-motion で動きを抑える配慮も忘れずに。完成イメージ(動くデモ)
↓ ボタンを押すと、左右からスライドインします:
HTML
フェードインさせたい要素に共通クラス slide-in と、方向を表す修飾クラス(slide-in--left など)を付けます。
<div class="slide-in slide-in--left"> <img src="image.jpg" alt="商品の写真"> <p>左からフェードインする文字</p> </div> <div class="slide-in slide-in--right"> <p>右からフェードインする文字</p> </div>
CSS(方向と初期状態)
初期状態を opacity: 0 + 横方向に少しずらした状態にし、is-visible が付いたら元の位置・不透明度に戻します。方向は translateX(左右)/ translateY(上下)で切り替えます。
.slide-in {
opacity: 0;
transition: opacity 0.8s ease-out, transform 0.8s ease-out;
}
/* 方向ごとの初期位置 */
.slide-in--left { transform: translateX(-60px); } /* 左から */
.slide-in--right { transform: translateX(60px); } /* 右から */
.slide-in--up { transform: translateY(60px); } /* 下から */
/* 画面に入ったら元の位置へ */
.slide-in.is-visible {
opacity: 1;
transform: translate(0, 0);
}
/* 動きを減らす設定では即表示(アクセシビリティ) */
@media (prefers-reduced-motion: reduce) {
.slide-in {
transition: none;
opacity: 1;
transform: none;
}
}
translateX(-100%) のように画面外まで大きくずらすと、横スクロールバーが出ることがあります。ずらす量は控えめ(数十px)にするか、親要素に overflow-x: hidden を指定してください。JavaScript(IntersectionObserverで発火)
要素が画面に入ったことの検知には IntersectionObserver を使います。一度表示したら unobserve() で監視を解除すると無駄がありません。
const elements = document.querySelectorAll(".slide-in");
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("is-visible"); // フェードイン開始
obs.unobserve(entry.target); // 一度きりにする
}
});
}, { threshold: 0.2 }); // 20%見えたら発火
elements.forEach((el) => observer.observe(el));
threshold で「どれくらい見えたら発火するか」を調整できます。IntersectionObserver の詳しい使い方はLazyload(遅延読み込み)完全ガイドでも解説しています。
なぜscrollイベントではなくIntersectionObserverなのか
古い記事では scroll イベントの中で getBoundingClientRect() を全要素ぶん呼ぶ方法をよく見かけますが、おすすめしません。
scrollは1回のスクロールで何十回も発火するため、そのたびに全要素の位置を計算するとカクつきの原因になる(レイアウトのコスト)IntersectionObserverはブラウザが効率的に交差を監視してくれるため、スクロールイベントを張る必要がなく軽い
同じくスクロールで発火させる演出として、数字をカウントアップさせる方法も IntersectionObserver を使っています。
よくある質問(FAQ)
opacity: 0 にして transition を設定し、IntersectionObserver で画面に入ったらクラスを付けて opacity: 1 に戻します。横からのスライドは transform: translateX() を併用します。transform を変えるだけです。左からは translateX(-60px)、右からは translateX(60px)、下からは translateY(60px)。表示時は translate(0, 0) に戻します。IntersectionObserver のコールバック内で、クラスを付けた直後に observer.unobserve(entry.target) を呼びます。これで再びスクロールしても発火しなくなります。@media (prefers-reduced-motion: reduce) の中で transition を切り、最初から表示状態にします。OSで「視差効果を減らす」を設定しているユーザーに配慮できます。まとめ
横からのフェードインは、CSSで「opacity: 0+横にずらした初期状態」を作り、IntersectionObserver で画面に入ったらクラスを付けて元に戻すのが現代的な作り方です。方向は translateX / translateY で自由に変えられます。
scroll イベントを毎回処理する古い方法より軽く、unobserve() で一度きりにでき、prefers-reduced-motion でアクセシビリティにも配慮できます。単純な「ふわっと表示」だけならふわっと表示させる方法も参考にしてください。
