ブログ

スクロールすると要素が固定され後続の要素が重なるアニメーション

おしゃれなサイトでよく見かける、スクロールに応じて要素が固定され後続の要素が重なるアニメーションの実装方法について解説しています。JavaScirptを使用した実装例とCSSで対応した実装例を紹介しています。

JavaScriptでの実装例

下記が実装例です。
※最後の要素が親要素を突き抜ける不具合がありましたので一部コードを修正しました(2026/01/02)。

JavaScriptでの実装例

スクロールに応じて数字を配置したボックスエリアが固定され、後続のボックスエリアが上に重なるようにしてアニメーションしています。

アニメーションを指定していない場合と比較して、より長くボックスエリアを見てもらうことができるので、重要なセクションなどに使用すると効果的かもれません。

解説

HTMLでは固定するセクションにdata-section属性を、その子要素にdata-section-inner属性を設定しています。最後のセクションは固定しないためdata属性は設定していません。

<section class="section" data-section>
<div class="section__inner" data-section-inner>1</div>
</section>
<section class="section" data-section>
<div class="section__inner" data-section-inner>2</div>
</section>
<section class="section" data-section>
<div class="section__inner" data-section-inner>3</div>
</section>
<section class="section" data-section>
<div class="section__inner" data-section-inner>4</div>
</section>
<section class="section">
<div class="section__inner">5</div>
</section>

CSSでは、固定するセクションとその子要素の高さをビューポートの高さに設定しています。このようにすることで画面いっぱいで重なるアニメーションを実現することができます。

親要素と小要素の高さを100svhで固定
.section {
height: 100svh;
/* 以下省略 */
}
.section__inner {
height: inherit;
/* 以下省略 */
}

JavaScriptの処理を言語化

JavaScriptではGSAPのScrollTriggerを使用しています。それぞれCDN等から読み込んでください。

ScrollTriggerのコード
const setFixed = () => {
gsap.set(inner, {
position: "fixed",
bottom: 0,
});
};
const setAbsolute = () => {
gsap.set(inner, {
position: "absolute",
bottom: "auto",
});
};
ScrollTrigger.create({
markers: true,
trigger: section,
start: "bottom bottom",
onEnter: setFixed,
onEnterBack: setFixed,
onLeave: setAbsolute,
onLeaveBack: setAbsolute,
});

section要素をトリガーにし、トリガーの底部がビューポートの底部に到達した時点でアニメーションが発動します。

onEnteronEnterBackではsetFixed関数を実行し、セクションの子要素のpositionを固定し、bottomを0に設定しています(つまりセクションの子要素は画面いっぱいで固定されます)。

onLeaveonLeaveBackではsetAbsolute関数を実行し、セクションの子要素のpositionの固定を解除し、bottomをautoに設定しています(つまりセクションの子要素は親要素の範囲に収まります)。

CSSでの実装例

CSSで実装した実装例は以下のとおりです。

CSSでの実装例

解説

HTMLではセクションを囲う親要素(wrapper)を新たに追加しています。セクションの子要素は削除し、セクション要素の中にダイレクトにコンテンツを入れています。

<div class="wrapper">
<section class="section">1</section>
<section class="section">2</section>
<section class="section">3</section>
<section class="section">4</section>
<section class="section">5</section>
</div>

CSSではセクションの高さを画面いっぱいに広げた上で、position: sticky;top: 0;を指定しています。これによりスクロールした際に画面上部に張り付いた状態を実現しています。

.section {
height: 100svh;
position: sticky;
top: 0;
display: grid;
place-items: center;
font-size: 10vw;
color: green;
}

2026年現在、ほとんどの主要ブラウザでposition: sticky;は問題なく機能するので、コードをシンプルに保ちたい場合はCSSでの実装がおすすめです。

ただし、より複雑なタイミング制御やアニメーション効果が必要な場合は、JavaScriptでの実装を検討すると良いでしょう。

参考サイト