Kodokon kodokon.com

モダンなインタラクション: scroll-snap、ビュートランジション

磁力のあるスクロール、ブラウザがアニメーションする状態遷移、そして prefers-reduced-motion の厳格な尊重を組み立てましょう。

11 分 · 3 問

このレッスンを Kodokon で開く

スクロールスナップには2人の登場人物が関わります。コンテナはスナップポート(scroll-padding によって縮められた可視領域)を定義し、scroll-snap-type を通じて戦略を決めます。proximity はスナップポイントの近くでのみスナップし、mandatory はプログラムによるスクロールの後でさえ、常にどれかのポイントに着地することを保証します。子は scroll-snap-align(scroll-margin で調整可能)で自身の揃え領域を宣言します。あまり知られていない細部として、素早いスワイプの最中、ブラウザは正当に複数のスナップポイントを飛ばすことがあります。scroll-snap-stop: always はそれぞれのポイントで止まることを強制します。ストーリー形式のカルーセルには不可欠です。

CSS
.carousel {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 1rem;
}
.carousel > article {
  flex: 0 0 80%;
  scroll-snap-align: center;
  scroll-snap-stop: always;
}
磁力のあるカルーセル。どのスライドも自身を中央に寄せ、どんなスワイプもスライドを飛ばせません。

View Transitions API は、ライブラリなしでDOMの状態変化をアニメーションさせます。document.startViewTransition(callback) は現在の状態の視覚的スナップショットを捕らえ、あなたのコールバック(DOMを変更する)を実行し、それから古い状態と新しい状態の間をアニメーションします。内部では、ブラウザはページの上に疑似要素のツリーを構築します。::view-transition は複数の ::view-transition-group 要素を含み、その各々が ::view-transition-old(スナップショット、画像に相当する置換コンテンツ)と ::view-transition-new(ライブの表現)を保持します。デフォルトはクロスフェードです。遷移の最中、ページは操作不能になります。

JAVASCRIPT
function applyUpdate(updateDom) {
  if (!document.startViewTransition) {
    updateDom();
    return;
  }
  document.startViewTransition(() => updateDom());
}
プログレッシブエンハンスメント。サポートがなければ、更新はアニメーションなしで単に適用されます。

本当の威力は view-transition-name から生まれます。名前を付けられた要素は、それ自身の遷移グループへと抽出されます。同じ名前を持つ要素が前後に存在すれば、ブラウザは2つの間で位置とサイズを自動的に補間します。サムネイルから全画面表示へのモーフィング効果が、アニメーションのJavaScriptを1行も書かずに実現するのです。各名前は、その瞬間のページ上で一意でなければなりません。さもなければ遷移は放棄されます。疑似要素は普通のCSSアニメーションでスタイリングします。

CSS
.hero-thumb { view-transition-name: hero; }

::view-transition-old(hero),
::view-transition-new(hero) {
  animation-duration: 0.4s;
  animation-timing-function: ease-in-out;
}
名前付きの hero グループが横取りされ、そのアニメーションの継続時間がカスタマイズされます。

残るはアクセシビリティの義務です。prefers-reduced-motion: reduce は、動きに敏感なユーザー(前庭障害、片頭痛)を示します。減らすことは取り除くことを意味しません。不透明度のフェードは許容範囲のままです。中和すべきなのは、大きな動き、ズーム、パララックス効果です。CSSにおける防御的なグローバル上書き(限りなくゼロに近い継続時間、前のモジュールで扱いました)はすでにご存じでしょう。ビュートランジションは第2の戦線を加えます。JavaScript側では、遷移やアニメーション付きスクロールを開始する前に window.matchMedia("(prefers-reduced-motion: reduce)") を問い合わせましょう。

JAVASCRIPT
const reduceMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches;

if (reduceMotion || !document.startViewTransition) {
  updateDom();
} else {
  document.startViewTransition(() => updateDom());
}
JavaScriptのセーフガード。ユーザーがより少ない動きを求めているとき、更新はモーフィングなしで適用されます。

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. scroll-snap-stop: always はカルーセルのアイテムに対して何をしますか?
    • タッチスクロールの慣性を無効化する
    • 素早いスワイプの最中でも、すべてのスナップポイントで止まることを強制する
    • 垂直方向の軸に沿ったスクロールをブロックする
  2. document.startViewTransition() は、あなたのコールバックを実行する前に何を捕らえますか?
    • 現在のDOMのディープクローン
    • 古い状態の視覚的スナップショット。::view-transition-old で再生される
    • ページ全体のシリアライズされたHTML
  3. JavaScriptからビュートランジションを開始する前に、どうやって prefers-reduced-motion を尊重しますか?
    • できない。この設定はCSSでしか読めない
    • window.matchMedia('(prefers-reduced-motion: reduce)').matches をテストし、一致したら startViewTransition なしで更新を適用する
    • startViewTransition に { motion: 'reduce' } オプションを渡す