@scroll-timelineを使ってCSSのみでスクロールアニメーションを実現する

UI開発者 板垣

本記事では、現在CSS Working GroupでEditor's Draftとして公開されているScroll-linked animationの仕様書を基に、@scroll-timelineの機能および使い方について解説します。

そもそもScroll-linked animationとは

Scroll-linked animation※1とはスクロールのオフセット値に関連付けたアニメーションを作成する機能の総称です。
CSSおよびJavaScript(Web Animations API拡張)で利用可能であり、これを活用することで、スクロールをしたらフワッと要素が浮き出る効果や、画面に視差効果を持たすことが今より簡単に実現できるようになります。

次の節からは、CSSで本機能を利用するために必要な「@scroll-timeline」について言及します※2。

@scroll-timelineとは

@scroll-timeline  { <declaration-list> }

at-rule」の1つで、スクロールアニメーションに必要な操作対象やアニメーションにかかる時間などを定義するためのルールです。

このルールは「timeline-name」という識別子と、「decoration-list」という記述子リストを受け取ります。
timeline-nameには@keyframesのように任意の文字列を識別子として指定することができ、これをanimation-timelineプロパティ※3に指定した値と同じにすることで、それらを対応付けさせることができます。decoration-listに指定できる記述子にはスクロールアニメーションに必要な情報を記述します。これについては次の項以降で説明いたします。

以下は@scroll-timelineおよびそれに関連する機能の記述例です。

.test {
    animation-name: test-animation;
    animation-timeline: test-timeline;
    animation-duration: 1s;
    animation-fill-mode: both;
}
@keyframes test-animation {
    0% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}
@scroll-timeline test-timeline {
    start: 0%;
    end: 100%;
    time-range: 1s;
}

decoration-listに指定できる記述子

本項ではdecoration-listに指定できる記述子を紹介いたします。

source

source記述子にはスクロールアニメーションを実行するための、スクロール可能な要素を指定します。
利用できる値はautonone、selector関数のいずれかで、初期値はautoです。

値にautoが指定されている場合は、html要素がセットされるため、任意の要素を指定したい場合はselector関数を使います。selector関数はid-selector引数として取ります(id-selector以外では機能しないので注意してください)。

orientation

orientation記述子にはスクロールタイムラインを進めるためのスクロール方向を指定します。
autoblockinlinehorizontalverticalの4つの値が利用可能で、初期値はautoです。

初期値のautoについては、これを指定した際に実際の値が何になるのかを仕様から読み取ることができませんでしたが、ScrollTimelineのインターフェースを確認すると初期値にblockが指定されているので、おそらくCSS側でもautoを指定した際にはblockとして動作するものと考えています。

ちなみにblockinlineというのは論理プロパティでの方向を指しています。

start

start記述子にはスクロールタイムラインの開始位置を指定します。値には<scroll-timeline-offset>が利用できます。

<scroll-timeline-offset>とは、以下の内容を指しています。

<scroll-timeline-offset> = auto | <length-percentage> | <element-offset>
<element-offset> = selector( <id-selector> ) [<element-offset-edge> || <number>]?
<element-offset-edge> = start | end

つまり、start記述子に指定できるのはauto(数値)%startend、selector関数、数値の6つのタイプになります。

各タイプが何を表すかは以下の通りです。

  • auto: sourceで指定した要素の先端(orientationによって変動)を設定します。
  • (数値)%: 指定した数値を基に開始位置を設定します。
  • selector関数: 引数で指定した要素を開始位置に設定します。すべての要素が対象というわけではなく、scroll-timelineを指定した要素の子孫要素のみが対象となります。
  • start: sourceで指定した要素の先端(orientationによって変動)を設定します。
  • end: sourceで指定した要素の終端(orientationによって変動)を設定します。
  • 数値: 0から1の間で開始位置を設定します。

end

end記述子にはスクロールタイムラインの終了位置を指定できます。値にはstart記述子と同じく<scroll-timeline-offset>を指定できます。

time-range

time-range記述子には時間(sもしくはms)を指定することで、スクロールによって表示されるアニメーションの進行度合いを設定できます。

いささかややこしいのですが、例えば以下のコードではtime-range5sと指定してあり、animation-durationには10sと指定されています。 この場合、スクロールによって表示されるアニメーションの範囲は@keyframesで言うところの50%までになります。

html {
    height: 200vh;
}
.box {
    width: 50px;
    height: 50px;
    background-color: gold;
    position: fixed;
    top: calc(50% - 25px);
    animation-name: box-animation;
    animation-timeline: box-animation-timeline;
    animation-duration: 10s;
    animation-fill-mode: both;
}
@keyframes box-animation {
    0% {
        left: 0%;
    }
    50% {
        left: 50%;
    }
    100% {
        left: 100%;
    }
}
@scroll-timeline box-animation-timeline {
    start: 0%;
    end: 100%;
    time-range: 5s;
}

以下のgif画像は上記のコードを実行したときの表示です。

スクロールバーが一番上にあるときは矩形がleft: 0%の位置にあり、一番下までいくと100%left: 100%の位置ではなく50%left: 50%の位置に移動していることがわかると思います。
スクロールバーが一番下にあるとき、left: 100%の位置に移動させたいのであれば、time-rangeanimation-durationと同じ値にします(今回の場合だと10s)。

この記述子の初期値はautoです。仕様を見る限り、end記述子の値を継承するものと考えているのですが、autoを指定すると動作しなくなってしまいます。

デモ

本デモでは、画面をスクロールすることで、画面中央に表示されているロゴが画面左上に移動するアニメーションと、テキストがフワッと表示されるアニメーションを実行しています。

また、ページ下部にあるボックスをスクロールすることで、それをトリガーとして、それの下にあるテキストがフワッと表示されるようになっています。この機能にはあまり実用性がありませんが、アニメーションのトリガー変更の例として見ていただければと思います。

閲覧されているブラウザが本機能に対応している場合はこちらのCodePenから実際の挙動をご確認いただけます。

おわりに

認知率、使用率ともに上昇中のCSS変数をはじめとする、プログラミング言語ライクな機能がだいぶCSSに追加および検討されてきている気がしています。
便利になっていく一方で、元来「装飾」を担当していたCSSと、「動的な処理」を担当していたJavaScriptの機能分担がわかりにくくなるのではと危惧しております。

どのような機能でも「なんとなく便利だから使う」のではなく、その機能が持つ特徴やどのような問題を解決するために作られたのかを理解して、「必要だから使う」ことを意識してこれらの機能を使用できるとよいのかもしれません。


※1 現時点では実験的機能であり、Chrome Canaryで「enable-experimental-web-platform-features」フラグを有効にしなければ機能しません。
※2 本記事に書かれている内容から仕様が変更される可能性もあるため、使用する際は十分な鮮度確認をお願いいたします。
※3 animation-timelineとは、アニメーションとタイムラインルールを関連付けさせるためのプロパティです。このプロパティが存在しない場合は、代わりにanimation-nameの値を見てアニメーションとタイムラインルールの対応付けを行います(現状だと実装には至っていないようです)。