【CSSだけ】メインビジュアルがヘッダーに変化!スクロールに応じて斬新な視覚表現をつくる方法[Scroll Timeline]

【CSSだけ】メインビジュアルがヘッダーに変化!スクロールに応じて斬新な視覚表現をつくる方法「ScrollTimeline, ViewTimeline]
このところ、CSSアニメーションの新概念「ScrollTimeline」と「ViewTimeline」を組み合わせて、さまざまな視覚効果を作る方法についての記事を書いています。

今回もまた、そのシリーズの記事です。今回は「メインビジュアルがスクロールに応じてヘッダーに変化」する挙動をテーマに、書いていきたいと思います。

メインビジュアルがスクロールに応じてヘッダーに変化!

早速ですが今回も、まずは次の動画をご覧ください。

↑この動画ですが、「ページのスクロールに応じて」、
メインビジュアルが「グローバルメニューを伴ったヘッダーに変化」します。

実はこちらも、これまでの記事のサンプルと同じく、「CSSだけ」で出来ています。
CSSアニメーションの新概念「ScrollTimeline」と「ViewTimeline」を組み合わせると、
こういった斬新な視覚効果もつくることができるんです!
※なお今回のサンプルでは、「ScrollTimeline」のみを使用しています。

実際に動かせるサンプル

次のリンクに、上記のセクションでご紹介した
CSSアニメーションの新概念「ScrollTimeline」を使用した、
動画と同じ挙動をするサンプルを用意しました。

「スクロールによってメインビジュアルが、グローバルメニューを伴ったヘッダーに変化」する様子を、ぜひ実際にお試しください!

メインビジュアルがスクロールに応じてヘッダーに変化するサンプル

コードのサンプル_html側

htmlのサンプルコードです。
本文は長いので…(中略)…としています。

文章のセクションの内容は、ある程度の長さがあれば内容は何でも良いです。

<div class="container">
  <section class="appear-section">
    <div class="appear-body">
      <h1 class="appear-logo">Villa Logo</h1>
      <h2 class="appear-catchcopy">Is this a Japanese peninsula or a tropical resort?</h2>
      <nav class="appear-navigation">
        <ul class="appear-menu-list">
          <li class="appear-menu-list-item"><a class="appear-link" href="#">ABOUT</a></li>
          <li class="appear-menu-list-item"><a class="appear-link" href="#">OUR SERVICE</a></li>
          <li class="appear-menu-list-item"><a class="appear-link" href="#">ROOM TYPE</a></li>
          <li class="appear-menu-list-item"><a class="appear-link" href="#">ACTIVITY</a></li>
          <li class="appear-menu-list-item"><a class="appear-link" href="#">RESERVATION</a></li>
        </ul>
      </nav>
    </div>
  </section>

  <section>
    <div class="common-inner">
      <h2 class="common-title">The Ultimate Getaway, Crafted Just for You</h2>
      <p class="common-text">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras non nunc
        lorem. Suspendisse tincidunt, elit quis volutpat volutpat, sapien libero
        fermentum felis, non luctus ipsum lectus quis lorem. Aenean euismod
        neque in nulla scelerisque, in faucibus magna dignissim. Duis placerat
        dolor leo, ut porta metus dapibus quis. Ut diam massa, commodo quis est
        id, eleifend fringilla ipsum. Suspendisse et ex a sem pharetra ornare.
        Proin venenatis laoreet nisl, nec laoreet mauris tincidunt in. In eget
        nisl commodo, hendrerit enim vitae, interdum ex. In rhoncus porttitor
        ornare. Pellentesque suscipit neque enim, ut auctor urna sollicitudin
        eu. Curabitur ullamcorper mollis malesuada.
     ...(中略)
      </p>
    </div>
  </section>

  <section>
    <div class="common-inner">
      <h2 class="common-title">Embrace the Freedom of the Perfect Holiday !</h2>
      <p class="common-text">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras non nunc
        lorem. Suspendisse tincidunt, elit quis volutpat volutpat, sapien libero
        fermentum felis, non luctus ipsum lectus quis lorem. Aenean euismod
        neque in nulla scelerisque, in faucibus magna dignissim. Duis placerat
        dolor leo, ut porta metus dapibus quis. Ut diam massa, commodo quis est
        id, eleifend fringilla ipsum. Suspendisse et ex a sem pharetra ornare.
        Proin venenatis laoreet nisl, nec laoreet mauris tincidunt in. In eget
        nisl commodo, hendrerit enim vitae, interdum ex. In rhoncus porttitor
        ornare. Pellentesque suscipit neque enim, ut auctor urna sollicitudin
        eu. Curabitur ullamcorper mollis malesuada.
     ...(中略)
      </p>
    </div>
  </section>
</div>

コードのサンプル_css側

続いて、CSSのサンプルです。大まかな説明はコメントアウトで記載しました。
(※やや長めなので、一旦メディアクエリ(スマホサイズのスタイルなど)は省いてます)

重要な点は、各構成要素の末尾の方にある

animation-timeline,
animation-range

↑このあたりです。
コードの後に続いて補足を記載します。

/* 基本構造。必ずしもこの限りでなくても動きます */
.container {
  padding: 50vh 0 0;
}
.common-inner {
  margin: 30rem auto 5rem;
  width: 1080px;
}
.common-title {
  color: rgb(250, 238, 155);
  font-family: serif;
  font-size: 3rem;
  font-weight: 500;
  text-align: center;
}
.common-text {
  color: #323232;
}

/* メインビジュアルのコンテナに対するアニメーション。 */
@keyframes transformTopBody {
  from {
    background-blend-mode: multiply;
    background-position: 50% 0;
    height: 100vh;
    font-size: 2rem;
    left: 0;
    position: fixed;
  }
  to {
    background-blend-mode: multiply;
    background-color: #727272;
    background-position: 50% 100%;
    height: 10vh;
    font-size: 1rem;
    left: 0;
    position: fixed;
  }
}
/* 変化したヘッダー内でグローバルメニューが出現するアニメーション。 */
@keyframes transformTopMenu {
  0% {
    display: flex;
    height: 0;
    justify-content: center;
    right: 10%;
    opacity: 0;
    position: absolute;
    top: 50%;
    transform: translate(0%, -50%);
    visibility: hidden;
  }
  90% {
    display: flex;
    height: auto;
    justify-content: center;
    right: 10%;
    opacity: 0.75;
    position: absolute;
    top: 50%;
    transform: translate(0%, -50%);
    visibility: visible;
  }
  100% {
    align-items: center;
    color: #fff;
    display: flex;
    justify-content: center;
    opacity: 1;
    position: absolute;
    right: 10%;
    top: 50%;
    transform: translate(0%, -50%);
    visibility: visible;
  }
}
/* キャッチコピー用のアニメーション。 */
@keyframes transformTopCatchCopy {
  from {
    font-size: 2rem;
    opacity: 1;
  }
  to {
    font-size: 0rem;
    opacity: 0;
  }
}
/* ロゴが出現するアニメーション。 */
@keyframes transformTopLogo {
  from {
    opacity: 0;
    visibility: hidden;
  }
  90% {
    opacity: 0.75;
    visibility: visible;
  }
  to {
    font-size: 1.875rem;
    opacity: 1;
  }
}

/* メインビジュアルのコンテナ */
.appear-body {
  animation: linear transformTopBody both;
  align-items: center;
  background: url(https://picsum.photos/1600/900?random=1) no-repeat 0 0/cover;
  display: flex;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
  height: 100vh;
  justify-content: center;
  position: relative;
  top: 0;
  width: 100%;

  /* ここが新要素! */
  animation-timeline: scroll();
  animation-range: 0vh 90vh;
}

/* ロゴのスタイル */
.appear-logo {
  animation: linear transformTopLogo both;
  color: #fff;
  font-family: "Parisienne", cursive;
  font-size: 1.875rem;
  position: absolute;
  left: 5%;
  
  /* ここが新要素! */
  animation-timeline: scroll();
  animation-range: 70vh 100vh;
}

/* キャッチコピー用スタイル */
.appear-catchcopy {
  animation: linear transformTopCatchCopy both;
  color: #fff;
  font-family: serif;
  font-size: 2rem;
  left: 50%;
  margin: 0;
  opacity: 1;
  padding: 0;
  position: absolute;
  text-align: center;
  text-shadow: 0 0 8px rgba(0, 0, 0, 0.75);
  top: 50%;
  transform: translate(-50%, -50%);
  width: max-content;

  /* ここが新要素! */
  animation-timeline: scroll();
  animation-range: 0vh 100vh;
}

/* 出現したグローバルメニューを操作できるようにz軸として手前に。100でなくても手前なら可。 */
.appear-navigation {
  z-index: 100;
}

/* 出現するグローバルメニューのスタイル */
.appear-menu-list {
  animation: linear transformTopMenu both;
  align-items: center;
  color: #fff;
  display: flex;
  height: 100%;
  gap: 0 2rem;
  justify-content: center;
  list-style-type: none;
  right: 10%;
  position: absolute;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
  width: fit-content;

  /* ここが新要素! */
  animation-timeline: scroll();
  animation-range: 70vh 100vh;
}

/* 出現するグローバルメニュー内のリンク用スタイル */
.appear-link {
  color: #fff;
  margin: 0;
  padding: 0;
  text-decoration: none;
  transition: opacity 0.3s;
  will-change: opacity; /* ←これは必須ではない */
}
.appear-link:hover {
  opacity: 0.7;
}

cssの補足

基本的にはコメントアウトに記載の通りです。
これまでのViewTimeline(ビュータイムライン)とScrollTimeline(スクロールタイムライン)を活用した記事と同じく、

1. まずは従来のCSSアニメーションの手法どおり、必要なアニメーションを定義する。
2. 続いて、動かしたい各要素にそのアニメーションを割り当てる。そして animation-timeline: scroll(); と指定することで、ページのルートスクロール(ページ全体のスクロール)を対象とした ScrollTimeline(スクロールタイムライン)を定義しています。
3. そして animation-range を使い、基準要素のどこから動き出してどこまでで動きが完了するかを設定しています。

上記の 2. を実行した時点で、 1. で定義した「CSSアニメーションの動く概念」が

「時間軸」 から
「スクロール軸」

の概念に変わっている、という点を認識することが重要です。

現時点での対応状況とポリフィル(補完して動かすJS)

毎度、このシリーズの記事では書いていることですが、
新しいCSSの概念「ビュータイムライン(ViewTimeline)」と「スクロールタイムライン(Scroll Timeline)」は、

とても「新しい」ということもあり、この記事を書いている時点での各ブラウザの対応状況は次の通りとなっています。
・Chrome 115 以上
・Edge 115 以上
・Firefox 未対応
・Safari 未対応

…という状況です。

ですが!これまでの記事でも書いた通り
ポリフィル(補完して動かすJS)もありまして、
次のJSを、動かしたい対象のhtml内に読み込んであげれば
未対応のブラウザでも動作させることが可能
です!
flackr/scroll-timeline

読み込ませ方は、上記リンク先ページの Read me 部分に記載があります。

animation-timeline, animation-range のMDNでの解説ページ

ご存知、MDNにもすでに、animation-timelineanimation-rangeの解説ページがあります。
MDNのこのページを読めば、大体の仕様がわかるかと思います。

animation-timeline – CSS: カスケーディングスタイルシート | MDN

animation-range – CSS: カスケーディングスタイルシート | MDN

↑基本的な仕様が網羅されていますので、一読すると、理解が深まるかと思います。

この記事のまとめ

今回は、Web制作・Webデザインの世界における
新しい概念「スクロールタイムライン(Scroll Timeline)」と、
もうひとつの新概念である「ビュータイムライン(ViewTimeline)」を組み合わせるとどんな事ができるか?
と言うことの続編として、
「メインビジュアルがスクロールに応じてヘッダーに変化」する例をもとに、記事を書きました。
※なお今回のサンプルでは、「ScrollTimeline」の概念のみを使用しています。

今回の記事は、背景画像を備えたメインビジュアルセクションがヘッダーに変化することについて焦点をあてた記事となりましたが、
画像ではなく、動画を流しても同様なことができます。

動画であれば、より目を引く表現ができることと思います。
使い所としては1ページもののメインビジュアルや、
下層ページの特定のコンテンツを紹介するページなどで使用しても、面白いかもしれません。
(※使用の際は、くれぐれも要件の確認をお忘れなく。
「ポリフィルを読み込ませることについて、クライアントサイドの承認は取れているか」
「対応ブラウザはこれとこれになるが、問題はないか」等、
新しい技術ですので、しっかり確認して、裏を取ってから試す事が必須です。)

今後も、「スクロールタイムライン(Scroll Timeline)」と「ビュータイムライン(ViewTimeline)」のすばらしい魅力が伝わりやすいような
記事を書いていきたく思ってますので、またぜひぜひ、続編をお待ちください。

この記事が皆さんのより良いWeb制作体験につながれば、嬉しく思います。

※この記事は内容の品質向上を目的に、随時更新を行う場合があります。


参考文献等:
Scroll-driven-animations(https://scroll-driven-animations.style/) – Copyright (c) Bramus (https://github.com/bramus), Chrome Developer Relations Engineer at Google
┗ License – https://www.apache.org/licenses/LICENSE-2.0.txt (Apache License Version 2.0)
scroll-timeline.js – Copyright (c) Robert Flack (https://github.com/flackr)
┗ License – https://www.apache.org/licenses/LICENSE-2.0.txt (Apache License Version 2.0)

この記事をシェアする: