スクロール連動で画像が固定&全画面に拡大するアニメーション

おしゃれなWebサイトでよく見かける、スクロールすると画像が固定され、画面いっぱいに拡大するアニメーションをGSAP(ScrollTrigger)を使って実装してみました。

実装例とともに、コードの仕組みを解説しています。

実装例

早速ですが以下が実装例です。

画像が画面いっぱいに拡大し固定されるアニメーション

画像が拡大・固定されることで、ユーザーの目を引くインパクトある表現が可能になります。特に、商品のビジュアルやブランドイメージを強調したいシーンで効果的です。今回は静止画で実装していますが、動画を使った演出にも応用できそうです。

ただし、このようなアニメーションは一時的にスクロール操作を“奪う”ような体験になるため、使い所を絞って演出のアクセントとして使うのが理想的でしょう。

コードの解説

アニメーションの仕組みを中心に解説していきます。

HTML/CSS

HTMLとCSSのコードは次のとおりです。

index.html
<div class="wrapper js-zoom-fixed-animation">
<figure class="container">
<div class="mask">
<img class="img" src="https://picsum.photos/1200/900/?random=1" alt="画像" />
</div>
</figure>
</div>
style.css
.wrapper {
width: 100%;
height: 350vh;
}
.container {
width: 100%;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
position: sticky;
top: 0;
}
.mask {
position: relative;
overflow: hidden;
aspect-ratio: 3 / 2;
}
.img {
width: 100%;
height: 100%;
position: absolute;
inset: 0;
object-fit: cover;
}

スクロール領域と要素の固定

CSSではcontainerを全画面表示にし、position: sticky;を指定することでビューポートの上部に張り付いて表示しています。親要素のスクロール領域の中で要素を固定するため、今回はfixedではなくstickyを使用しています。

一方、親要素であるwrapperでは高さ(今回は350vh)を設定し、子要素が追従可能な領域を明示的に確保しています。この高さがあることで、containerを固定したままマスクと画像のアニメーションを発動させることができます。

画像のマスクを用意

mask要素にはoverflow: hidden;を指定し、マスクからはみ出た部分を非表示にしています。これにより、マスクのサイズが変化すると画像の見える領域も連動して変化します。

JavaScript

JavaScriptのコードは次のとおりです。

script.js
const wrapper = document.querySelector(".js-zoom-fixed-animation");
const container = wrapper.querySelector(".container");
const mask = wrapper.querySelector(".mask");
const img = wrapper.querySelector(".img");
// スクロールに応じてマスクのサイズを広げる
gsap.fromTo(
mask,
{
width: "40%",
},
{
width: "100%",
height: () => window.innerHeight + "px",
scrollTrigger: {
trigger: container,
endTrigger: wrapper,
start: "top top",
end: "bottom bottom",
scrub: 1.2,
invalidateOnRefresh: true,
},
}
);
// スクロールに応じて画像のサイズを縮小
gsap.fromTo(
img,
{ scale: 2 },
{
scale: 1,
scrollTrigger: {
trigger: container,
endTrigger: wrapper,
start: "top top",
end: "bottom bottom",
scrub: 1.2,
invalidateOnRefresh: true,
},
}
);

スクロール操作に応じて2つの異なるアニメーションを設定しています。マスクと画像、それぞれに異なる動きを与えることで、よりダイナミックな視覚効果を実現しています。

対象要素アニメーションの内容
.mask(マスク)画像を覆うマスクのサイズを拡大する
.img(画像)画像自体のスケールを縮小する(拡大から通常サイズに戻す)

マスクのサイズを拡大する処理

マスクは幅と高さの両方を変化させています。

プロパティ初期値アニメーション完了時
width40%100%
height成り行きビューポートの高さ

widthは40%から100%に、heightは初期値(aspect-ratioで自動計算された値)からビューポートの高さに拡大しています。

またScrollTriggerのオプションであるinvalidateOnRefresh: trueを指定することで、ブラウザのリサイズ時にwindow.innerHeightの値を再計算しています。

このようにwidthheightを同時に拡大することで、マスクの拡大がより自然に、かつ全画面へと切り替わるように見せることが可能となります。

画像のスケールを縮小する処理

次に、画像そのものに対してscaleアニメーションを設定しています。初期状態では画像を拡大表示(scale: 2)させ、アニメーションの完了時には等倍(scale: 1)に戻しています。

プロパティ初期値アニメーション完了時
scale21

このアニメーションは、マスクの拡大と組み合わせることで、「マスクが拡がり、画像はちょうどよく収まる」ように見せる工夫です。視覚的なバランスの問題で追加している処理なので、必要に応じて省略可能です。

まとめ

以上、GSAPを用いた画像拡大・固定アニメーションの実装方法でした。

スクロール領域の高さ、scrubの値、画像のscaleの初期値を変更することで、動きをアレンジすることが可能です。デザインのアクセントとして使うときにぜひ参考にしてみてください。

この記事を書いた人

写真:ブール 代表 西川雅人

Masato Nishikawa

ウェブ制作会社・デザイナー様向けに、コーディングやWordPress実装を承っているフリーランスです。繁忙期のサポートや、継続的な外注先をお探しでしたら、お気軽にお声がけください。

コーディングや
WordPress実装の
ご相談を承っています。

新規制作から運用フェーズの修正まで柔軟に対応します。
お見積りや実装可否の確認など、お気軽にご連絡ください。