判定: 保留

スクロールに反応する CSS アニメーション

このサイトが今 IntersectionObserver でやっている処理を、CSS の animation-timeline で置き換えられるか。

デモ 1: 読み進み率のバー

ページ最上部の細いバーが、スクロール量に合わせて伸びます。画面を上下させると、そのまま追従します。

@supports (animation-timeline: scroll()) {
  .sda-progress span {
    transform-origin: 0 50%;
    animation: sda-grow linear both;
    animation-timeline: scroll(root block);
  }
}
@keyframes sda-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }

デモ 2: 入ってきた分だけ進むフェードイン

下の 4 枚は、画面に入った量そのものが進行度になっています。ゆっくりスクロールすると、途中で止められます。

いま、このサイトは JavaScript でやっている

Terralien の共通レイアウトには IntersectionObserver が入っていて、セクションが画面に入ったら class を付けてフェードインさせています。20 行ほどのスクリプトです。

CSS だけにすると何が変わるか

animation-timeline: view() を使うと、要素が画面に入った量そのものをアニメーションの進行度にできます。「入ったら再生」ではなく「入った分だけ進む」なので、スクロールを戻すとアニメーションも戻ります。

スクロールを戻してみてください

JavaScript 版は一度発火したら戻りません(observe を解除しているため)。この違いは好みの問題ではなく、設計の違いです。どちらが良いかは中身によります。

未対応ブラウザでは

@supports で囲ってあるので、対応していないブラウザにはアニメーションの指定そのものが届きません。テキストは最初から見えています。「透明のまま出てこない」という最悪の壊れ方をしないための書き方です。

@supports (animation-timeline: view()) {
  .sda-card {
    animation: sda-rise linear both;
    animation-timeline: view();
    animation-range: entry 5% cover 30%;
  }
}
@keyframes sda-rise {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: none; }
}

JavaScript 版との比較

観点CSS(今回)IntersectionObserver(現行)
追加する JS0 行約 20 行(共通レイアウトに常駐)
スクロールを戻したときアニメーションも戻る戻らない(一度きり)
要素の指定CSS セレクタセレクタ文字列をスクリプト側に持つ
未対応時最初から見えている最初から見えている(同じくフォールバック済み)
置き換えの可否現行の「一度きりで止める」挙動を維持したいなら、CSS だけでは同じにならない

動きが苦手な読者への配慮

prefers-reduced-motion: reduce のとき、バーもフェードインも止めています。止めた結果として情報が消えないこと(=最初から全部見えていること)まで含めて確認するのが要件です。

判定を「保留」にした理由: 共通レイアウトのスクリプトを消せるかは、まだ決められません。現行は「一度出たら戻さない」挙動で、本文の読みやすさはそちらのほうが素直です。バーだけ先に採用するのが現実的だと考えています。