代表的な面接トピック

フロントエンド面接:スクロール連動アニメーションを安全に構築するにはどうすればよいですか?

フロントエンド普通
Offer.cc 編集チーム公開日 更新日

質問

ある製品ページで、進捗インジケーターとビューポートに入ったときにアニメーションするカードが必要です。スクロール連動の実装を設計し、scroll progress timeline と view progress timeline の違いを説明し、古いブラウザをサポートしつつ、パフォーマンスとアクセシビリティを維持してください。

プロンプトとユースケース

目標は、スクロールイベントごとに JavaScript ハンドラーを実行することなく、アニメーションの進捗をスクロールに連動させることです。CSS スクロール連動アニメーションは、animation-timelinescroll()view() を通じて scroll-progress および view-progress タイムラインを提供します。実装には依然としてフォールバック、視覚効果の抑制(reduced-motion)時の挙動、包含(containment)の判断、および明確なブラウザサポート方針が必要です。

面接官が見ているポイント

  • スクロールコンテナの進捗と要素の可視性進捗を区別できているか。
  • animation-timeline の宣言を animation ショートハンドの後に配置しているか。
  • レイアウトを発生させるプロパティよりも transform や opacity を優先して選択しているか。
  • フィーチャークエリまたはアニメーションなしのベースラインを提供しているか。
  • prefers-reduced-motion を尊重し、動きがなくても読めるコンテンツを維持しているか。
  • 長いページ、ネストされたスクローラー、sticky 要素、モバイルパフォーマンスを測定しているか。

回答前の確認事項

  • 進捗はページ、ネストされたスクロールコンテナ、それともビューポートに入る要素のどれに連動していますか?
  • アニメーションは状態を伝える必要がありますか、それとも装飾目的ですか?
  • どのブラウザや組み込み WebView が対象範囲に含まれますか?
  • 視覚効果の抑制を設定しているユーザーには何を表示すべきですか?
  • デザインを静的なプログレスバーや静的な表示にフォールバックさせてもよいですか?
  • 包含ブロックを変更する sticky、transform、または仮想化された祖先要素はありますか?

30秒の回答フレームワーク

「ページの進捗には scroll-progress タイムラインを使用し、スクローラーに入るカードには view-progress タイムラインを使用します。静的で読み取り可能なベースラインを定義した上で、フィーチャークエリ内でアニメーションショートハンドの後に animation-timeline を宣言して段階的に拡張します。transform や opacity をアニメーション化し、ネストされたスクローラーや sticky 要素をテストし、prefers-reduced-motion の下では不要な動きを無効化します。リリース前にサポート状況、フレームコスト、キーボード操作性、コンテンツの理解しやすさを検証します。」

ステップごとの詳細な回答

ステップ 1: タイムラインを選択する。

アニメーションがコンテナのスクロール範囲に対応する場合は scroll-progress タイムラインを使用します。アニメーションがスクロールポート内を通過する要素の移動に対応する場合は view-progress タイムラインを使用します。

ステップ 2: ベースラインを構築する。

まず、進捗インジケーターとカードを有用な静的状態でレンダリングします。アニメーションを無視するブラウザであっても、コンテンツ、順序、コントラスト、およびコントロールを表示できる必要があります。

ステップ 3: CSS による機能拡張を追加する。

css
@supports (animation-timeline: scroll()) {
  .progress {
    animation: grow-progress linear both;
    animation-timeline: scroll(root block);
  }

  .card {
    animation: reveal linear both;
    animation-timeline: view(block);
    animation-range: entry 0% cover 35%;
  }
}

ショートハンドはタイムライン関連のサブプロパティをリセットするため、ショートハンドの後に animation-timeline を宣言します。ネストされたスクロールコンテナによって最も近いスクローラーが曖昧になる可能性がある場合は、タイムラインに明示的な名前を付けます。

ステップ 4: アニメーション化するプロパティのコストを低く保つ。

opacity と transform を優先します。プロファイリングによってコストが許容可能であることが証明されない限り、長いリストで width、height、top、left、または大きなシャドウをアニメーション化することは避けてください。当て推量ですべてのカードに will-change を追加しないでください。

ステップ 5: 視覚効果の抑制(reduced motion)に対応する。

css
@media (prefers-reduced-motion: reduce) {
  .progress,
  .card {
    animation: none;
    transform: none;
    opacity: 1;
  }
}

コンテンツと進捗の意味は、アニメーションなしでも利用可能でなければなりません。長いトランジションを単に短くするよりも、控えめな即時状態や静的なインジケーターを用意する方が安全です。

ステップ 6: ジオメトリとサポートをテストする。

ルートスクローラー、ネストされた水平・垂直スクローラー、sticky 要素、動的コンテンツ、ズーム、キーボードナビゲーション、モバイルビューポートの変更をテストします。機能検出は拡張を制御するために使用し、ベースラインを隠すために使用してはなりません。

ステップ 7: 測定して観察する。

DevTools のパフォーマンストレースを使用して、スタイル、レイアウト、ペイント、およびコンポジターの処理を確認します。長いリストや低スペックのモバイルデバイスでテストし、タイムラインがサポートされていない場合や中断された場合にコンテンツが読めなくならないかを注視します。

質の高い回答例

「ページのプログレスバーには、その値がルートスクロール範囲であるため scroll(root block) を使用します。各カードには、自身の可視性に応じて表示されるべきであるため、entry から cover までの範囲を持つ view(block) を使用します。まずカードを通常どおりレンダリングし、次に @supports 内でタイムラインを追加します。その際、宣言はアニメーションショートハンドの後に配置します。動きは transform と opacity に限定し、視覚効果の抑制を希望するユーザー向けには不要なアニメーションを削除し、ネストによって最も近いコンテナが曖昧になる場合はスクローラーに名前を付けます。パフォーマンストレースを用いて、キーボードアクセス、sticky 要素や動的コンテンツ、未対応ブラウザ、およびモバイルでの長いリストをテストします。」

よくある間違い

  • フレームごとにスクロールイベントを使用する → ハンドラーの処理がスクロールと競合する可能性があります → 関係が宣言型である場合は CSS タイムラインを優先します。
  • scroll()view() を混同する → アニメーションが不適切なタイミングで開始されます → タイムラインをコンテナの進捗または要素の可視性に適切にマッピングします。
  • ショートハンドの前に animation-timeline を配置する → ショートハンドによってリセットされる可能性があります → タイムラインはショートハンドの後に宣言します。
  • デフォルトでレイアウトプロパティをアニメーション化する → 長いページでは負荷の高い処理が発生する可能性があります → transform と opacity から始め、その後にプロファイリングを行います。
  • 視覚効果の抑制の対応なしに動きを適用してリリースする → ユーザーが設定を制御できなくなります → 静的な状態を提供します。
  • アニメーションがサポートされるまでコンテンツを非表示にする → 未対応ブラウザで情報が失われます → まずベースラインを構築します。
  • 最も近いスクローラーが正しいと決めつける → ネストされたコンテナによってタイムラインが変化します → 意図したスクローラーに名前を付けるかテストします。

フォローアップの質問と回答

フォローアップ 1: scroll()view() の違いは何ですか?

scroll() はスクロールコンテナの進捗を追跡します。view() はそのコンテナのスクロールポート内における要素の可視性の推移を追跡します。

フォローアップ 2: なぜタイムラインはショートハンドの後に記述しなければならないのですか?

animation ショートハンドはタイムライン関連のサブプロパティをリセットするため、先行するタイムライン宣言がクリアされてしまう可能性があるからです。

フォローアップ 3: フォールバックは何ですか?

コンテンツを最終的な読み取り可能状態で表示したままにし、製品要件で本当に必要な場合にのみ静的な進捗インジケーターまたは小規模な JavaScript による機能拡張を使用します。

フォローアップ 4: 視覚効果の抑制(reduced motion)にはどのように対応しますか?

不要なアニメーションを無効にし、transform を削除して、静的なスタイルで同じコンテンツとステータスの意味を維持します。

フォローアップ 5: height をアニメーション化することはできますか?

プロファイリングを行い、明確な必要性がある場合に限られます。通常、transform と opacity はレイアウト処理を回避しますが、height のアニメーション化は兄弟要素全体のレイアウトを無効化(再計算)する可能性があります。

フォローアップ 6: ネストされたスクローラーでは何が起こりますか?

適用される最も近いスクロールコンテナがページと異なる場合があります。ジオメトリをテストし、意図したタイムラインに名前を付けるか、正しいスクローラー引数を使用してください。

フォローアップ 7: 本番環境ではどのように検証しますか?

ブラウザのサポート利用率、代表的なデバイスでのパフォーマンストレース、視覚効果の抑制時の挙動、アクセシビリティチェックを記録します。タイムラインがサポートされていない場合や中断された場合でも使用できるように、静的なベースラインを維持します。

公開情報ソース

関連する質問