スクロールをトリガーにしたアニメーションは scroll-driven animations が便利だ。ただ、この scroll-driven animations を完全制覇するには、アニメーションがどこで始まってどこで終わるかの定義をマスターしなければならない。
scroll-driven animations を JavaScipt を使う方法でやってみる。フルスクリーンで起動して文字がヘッダーに格納されるシンプルなものから作ってみた。
これにもっと磨きをかけるために、ヘッダー部分にアイコンやリンクを表示して、フルスクリーンに背景画像を配置してみたのがこれだ。
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>
例えば、タグやクラス名の後ろに「:」(ダブルコロン)を付け、疑似クラス名を記述して使用する。擬似クラスは、文章構造の範囲外となる情報や、特定の要素のみに変更を加える場合に、単体のセレクタでは表現できないものに対して使われる。
疑似クラスには、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 の疑似要素では要素の前や後ろに追加する、1文字目や1行目だけを変更するなどの「指定した要素の一部」に対してスタイルの指定をするものだ。 疑似クラスでは「指定した要素全体」に対してスタイルを適用する。つまり、指定する要素の部分と全体、そのスタイルの適用範囲が異なる。
(疑似要素と疑似クラスの併用の例)
…div:hover::after のように「:疑似クラス::疑似要素」という順で記述する。
h2.sample::first-letter {
color: #ff0000;
}
対象の要素の一部を指定して装飾を適用する、もしくは対象の要素に擬似的に要素を追加して装飾を適用するセレクタを擬似要素という。記述としては要素の後に:(コロン)を二つ付けて指定する。CSS2まではコロンは一つだったが、擬似クラスと識別するため、CSS3からコロンを二つ記述するようになった。
スクロールに併せてフロント画像の円形画像の中身がコマ送りされて画面上部の吸着した細いヘッダーに移動して納まるギミックの備わったページに不具合があったので修正した。(2024/5/2)
animation-range は scroll-driven animations をコントロールする上で重要な部分である。定義する方法は多くあり過ぎて、分かるものは分かるが微妙なものもあり、法則性を見出せないでいる。分かりずらくしているのはスクロールは下方向、上方向の二種類あり「animation-fill-mode: both;」を伴うケースだ。
「animation-fill-mode: both;」は、アニメーションが開始される前と終了後どのような状態(モード)で待機(満たす)かを指定するプロパティだ。
「both」は backwards と forwards の両方の状態を適用される。このアニメーション再生の送りと戻すの状態が理解をややこしくしているのだが…。
このセクションでは、扱いやすいタイプをピックアップして、animation-range: の値を以下の内容で比較してみた。以下の画像をタップしてほしい。
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%)」を指定した例題をやってみようと思う。
この例題の 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()
今まで比較してきたページ(※検証)でも同様の検証をしてみた。定義通りの挙動を確認できた。次の画像をタップするとページが閲覧できる。
animation-range は CSS の一括指定プロパティで、タイムラインに沿ったアニメーションの適用範囲の先頭と末尾を設定する。 つまり、タイムラインのどこでアニメーションが始まり、どこで終わるかを設定するために使用する。
以下のように animation の一括指定を宣言した後に animation-range を宣言しないと、その効果を得ることができない。
animation: text-stroke 4s linear 1;
animation-timeline: view();
animation-range-end: cover 80%;