CSS / animation

scroll-driven animations と animation-range

─ Guide to page sections ─

スクロールをトリガーにしたアニメーションは scroll-driven animations が便利だ。ただ、この scroll-driven animations を完全制覇するには、アニメーションがどこで始まってどこで終わるかの定義をマスターしなければならない。

animation-range をマスター
アニメーションがどこで始まってどこで終わるかの定義

First

フルスクリーンの表示をスクロールするとヘッダーに収める

scroll-driven animations を JavaScipt を使う方法でやってみる。フルスクリーンで起動して文字がヘッダーに格納されるシンプルなものから作ってみた。

PC(landscape) と モバイル(portrait) のスクリーンショット
フルスクリーンで起動、スクロールでヘッダーに格納される
PC(landscape) と モバイル(portrait) のスクリーンショット
フルスクリーンに背景画像を配置したものをスクロールするとヘッダーに画像を格納してアイコンやリンクを表示する

これにもっと磨きをかけるために、ヘッダー部分にアイコンやリンクを表示して、フルスクリーンに背景画像を配置してみたのがこれだ。

scroll-driven animations を動かす JavaScript の構文

JavaScript の構文は以下のようになる。ある条件下で「toggleClass」を追加したり削除したりするもので、JavaScript では、この手法はお馴染みのものだ。

ここではどういう内容を JavaScript が指示しているのかを判断できるようにひとつひとつ //コメント を加えた。

<script>
const body = document.body; //<body>へのアクセスを行う
const toggleClass = "is-sticky"; 
  //toggleClass を is-sticky に宣言する

window.addEventListener("scroll", () => { 
  //addEventListenerメソッドを使ってscrollイベントを登録する時の書き方
  const currentScroll = window.pageYOffset; 
  //Web 文書が垂直方向にどれだけスクロールされたかをピクセル単位で返すプロパティ 
  //currentScroll は、現在のスクロール位置(単位:ピクセル)を表す変数
  if (currentScroll > 0) {
    body.classList.add(toggleClass);
  //body に追加したい先の要素 toggleClass を付け加える
  //window.pageYOffset は、Window のプロパティで、ブラウザの上端を基準とした縦方向のページのスクロール量を返す
  } else {
    body.classList.remove(toggleClass); 
  //toggleClass を取り除く
  }
});
</script>

Second

疑似クラスについて

例えば、タグやクラス名の後ろに「:」(ダブルコロン)を付け、疑似クラス名を記述して使用する。擬似クラスは、文章構造の範囲外となる情報や、特定の要素のみに変更を加える場合に、単体のセレクタでは表現できないものに対して使われる。

疑似クラス
疑似クラスの構文

疑似クラスには、hover、link、visited、nth-child、first-child、last-child、not、before、after などがある。馴染みのないところでは、 :any-link というのもある。

/* :link または :visited に該当するすべての要素を選択 */
:any-link {
  color: green;
}

さて、ここで疑似クラスを取り上げたのは、次のセクション(Third)で扱うページで使われる CSS の内容の「h1:before」の備忘録である。疑似クラスに animation-timeline: view(); を設定して、ユーザーのスクロールに合わせて要素を動かすことができるのだが、こういう使い方があるとは思わなかった。

h1 {
  position: relative;
  font-family: "Roboto", sans-serif;
  font-size: 3vw;
  color: transparent;
  -webkit-text-stroke: 1px tomato;
  margin-left: -150px;
}
h1:before {
  position: absolute;
  content: attr(data-text);
  top: 0;
  left: 0;
  width: 0;
  height: 100%;
  background-color: orange;
  color: tomato;
  border-right: 15px solid tomato;
  overflow: hidden;
  -webkit-text-stroke: 1px tomato;
  animation: text-stroke 4s linear 1;
  animation-timeline: view();
  animation-range: entry 100% cover 60%; 
}

CSS の疑似要素と疑似クラスとの違い

CSS の疑似要素では要素の前や後ろに追加する、1文字目や1行目だけを変更するなどの「指定した要素の一部」に対してスタイルの指定をするものだ。 疑似クラスでは「指定した要素全体」に対してスタイルを適用する。つまり、指定する要素の部分と全体、そのスタイルの適用範囲が異なる。

(疑似要素と疑似クラスの併用の例)
…div:hover::after のように「:疑似クラス::疑似要素」という順で記述する。

疑似要素

h2.sample::first-letter {
        color: #ff0000;
        }

対象の要素の一部を指定して装飾を適用する、もしくは対象の要素に擬似的に要素を追加して装飾を適用するセレクタを擬似要素という。記述としては要素の後に:(コロン)を二つ付けて指定する。CSS2まではコロンは一つだったが、擬似クラスと識別するため、CSS3からコロンを二つ記述するようになった。


scroll-driven animationsスクロールに併せてフロント画像の円形画像の中身がコマ送りされて画面上部の吸着した細いヘッダーに移動して納まるギミックの備わったページに不具合があったので修正した。(2024/5/2)


Third

animation-range:

animation-range は scroll-driven animations をコントロールする上で重要な部分である。定義する方法は多くあり過ぎて、分かるものは分かるが微妙なものもあり、法則性を見出せないでいる。分かりずらくしているのはスクロールは下方向、上方向の二種類あり「animation-fill-mode: both;」を伴うケースだ。

「animation-fill-mode: both;」は、アニメーションが開始される前と終了後どのような状態(モード)で待機(満たす)かを指定するプロパティだ。

「both」は backwards と forwards の両方の状態を適用される。このアニメーション再生の送りと戻すの状態が理解をややこしくしているのだが…。

このセクションでは、扱いやすいタイプをピックアップして、animation-range: の値を以下の内容で比較してみた。以下の画像をタップしてほしい。

  • (1).animation-range-end: cover 80%;
  • (2).animation-range: 記載なし;
  • (3).animation-range: entry 10% exit 50%;
  • (4).animation-range-end: cover 20%;
  • (5).animation-timeline: view(block 50% 10%);
  • (6).animation-range: cover;
animation-range 検証
animation-range 検証

Fourth

view(block 50% 10%) とは

scroll-driven animations には animation-timelineプロパティに scroll() 関数もしくは view() 関数を指定する。view() 関数を指定したものは、スクロールできるコンテナのスクロールポートを基準としてアニメーションを指定できるタイムラインだ。

view() 関数 には、animation-timeline: view(block); 、animation-timeline: view(auto); 、animation-timeline: view(block auto);、があってこれは全て既定らしい。

scroll-driven animations をここまで掘り下げてきて、animation-timeline: と animation-range の関係性がどうも釈然としない。

そこで、打開策として取り出したのが、view() 関数の値に、「view(block 50% 10%)」を指定した例題をやってみようと思う。

View Progress Timeline
animation-timeline: view(block 50% 10%)

この例題の html は次のようになる。重要なところは、赤字である「class="animation"」である。ここにアニメーションと animation-timeline: が定義される。

<body>
  <div class="content">
    <h1>Content</h1>
    <p>以下のコードでは、 subject クラスを持つ…アニメーションすることを宣言します。</p>
    <p>起動画面より…縮小していきます。</p>
    <div class="subject-container">
      <div class="subject animation">
      </div>
    </div>
    <p>覚えておく…続くということです。</p>
    <div style="height:600px;">
    </div>
    </div>
</body>

CSS は次のように定義されている。

.subject-container {
  border: 2px dashed black;
  width: 300px;
  margin: 0 auto;
}
.subject {
  width: 300px;
  height: 200px;
  background-color: tomato;
  color: #fff;
  font-weight: 900;
  font-size: 1.2rem;
}
.animation {
  animation-timeline: view(block 50% 10%);
  animation-name: grow;
  animation-fill-mode: both;
  animation-duration: 1ms; /* Firefox では、アニメーションを適用するために必要 */
  animation-timing-function: linear;
}

animation-timeline: view(block 50% 10%); は 「.animation」 に定義している。アニメーション 「grow 」は subject 要素を大きくしたり小さくしたりする。animation-timeline: view(block 50% 10%) を設定することで、スクロールする祖先(この場合は文書内のルート要素)が指定された進行タイムラインに沿ってアニメーションすることを宣言している。

起動画面よりスクロールしている間(下方向へにスクロールバーが移動している間)、50% 10% のインセット値によってアニメーションが下から 10% で始まり、上から 50% で完了する。

逆に、起動画面へ戻すスクロールをする間(上方向へスクロールバーが移動すると)、アニメーションは逆方向へ進み、上から 50% で始まり、アニメーションを逆方向へ移動し、下から 10% で終わる。つまり、アニメーションが逆方向に進むにつれて、 subject は縮小して消滅する。

@keyframes grow {
  from {
    transform: scaleX(0);
  }

  to {
    transform: scaleX(1);
  }

animation-timeline: view(block 50% 10%) の挙動

起動画面よりスクロールしている間(下方向へにスクロールバーが移動している間)、50% 10% のインセット(inset は CSS のプロパティで、 top, right, bottom, left に対応する一括指定)値によってアニメーションが下から 10% で始まり、上から 50% で完了する。

参考ページ:mdn web docs view()


Fifth

view(block 50% 10%)実例の検証

今まで比較してきたページ(※検証)でも同様の検証をしてみた。定義通りの挙動を確認できた。次の画像をタップするとページが閲覧できる。

animation-timeline: view(block 50% 10%)
animation-timeline: view(block 50% 10%)
animation-timeline: view(block 10% 10%)
animation-timeline: view(block 10% 10%)

animation-range は CSS の一括指定プロパティで、タイムラインに沿ったアニメーションの適用範囲の先頭と末尾を設定する。 つまり、タイムラインのどこでアニメーションが始まり、どこで終わるかを設定するために使用する。

以下のように animation の一括指定を宣言した後に animation-range を宣言しないと、その効果を得ることができない。

animation: text-stroke 4s linear 1;
animation-timeline: view();
animation-range-end: cover 80%; 
animation-range: entry 100% cover 60%
animation-range: entry 100% cover 60%