インタラクティブなコンテンツ表示

CSS マスクと登録済みのカスタム プロパティを使用して、パフォーマンスに優れたインタラクティブな表示効果をサイトに追加します。円形グラデーションをマスクとして使用し、そのストップ値を登録することで、最小限の JavaScript でユーザーのポインタを追跡しながら、エントリと終了をスムーズに移行できます。

実装

1. カスタム プロパティを登録する

グラデーションのストップ値の補間をスムーズにするには、@property を使用して変数を登録する必要があります。これにより、ブラウザのエンジンにデータ型が通知され、更新中に値が遷移できるようになります。

/* Register the spotlight inner and outer sizes to enable interpolation */
 @property --inner-size{
  syntax: "<length-percentage>";
  inherits: true;
  initial-value: 0px;
}
@property --outer-size{
  syntax: "<length-percentage>";
  inherits: true;
  initial-value: 0px;
}

ポインタの位置をトラッキングするカスタム プロパティは遷移する必要がないため、登録する必要はありません。

2. マスキング レイヤを定義する

表示する要素に mask-image を適用します。登録済みのプロパティを参照する radial-gradient を使用します。

.reveal-layer {
  /* Only transition the size properties, NOT the position variables */
  transition: --inner-size 0.2s ease-in-out, --outer-size 0.2s ease-in-out;  

  /* The spotlight is defined by the transparent center of the mask */
  mask-image: radial-gradient(
    circle at var(--mouse-x) var(--mouse-y),
    black var(--inner-size, 0%),
    transparent var(--outer-size, 0%)
  );

  /* Ensure the mask doesn't repeat if the element is large */
  mask-repeat: no-repeat;

  /* Make the mask layer non-interactive */
  pointer-events: none;
}

/* Update the gradients stops on interaction */
.reveal-layer:hover {
  --inner-size: 100px;
  --outer-size: 120px;
}  

3. JavaScript で座標を更新する

ポインタの位置を追跡し、CSS 変数を更新します。プロパティが登録され、transition が定義されているため、ポインタイベントの頻度が低くても、スポットライトはスムーズに移動します。

const container = document.querySelector('.container');
// Store the container's bounding rect
let rect = container.getBoundingClientRect();
// Update the rect when the container is resized
const resizeObserver = new ResizeObserver(()=>{
  rect = container.getBoundingClientRect();
})
resizeObserver.observe(container);

container.addEventListener('pointermove', (e) => {
  // Calculate position as a percentage of the container.
  const x = ((e.clientX - rect.left) / rect.width) * 100;
  const y = ((e.clientY - rect.top) / rect.height) * 100;

  // Update the registered properties
  container.style.setProperty('--mouse-x', `${x}%`);
  container.style.setProperty('--mouse-y', `${y}%`);
});

4. ユーザー補助と操作

必須のユーザー補助保証: このパターンは、ポインタ操作に依存して視覚的なスポットライトを表示します。ポインタ ユーザー向けの必須ではない視覚的な拡張機能としてのみスポットライト レイヤを使用し、すべての基盤となるコンテンツが、基盤となるレイアウトでデフォルトで完全に表示され、判読可能で、キーボードで個別にアクセスできることを保証しなければなりません。キーボードのみを使用するユーザーや支援技術を使用するユーザーに対して、重要なコンテンツを隠したり、アクセスを制限したりするために、この効果を使用しないでください。

  • ポインター イベント: マスク オーバーレイ レイヤに pointer-events: none を設定して、標準のクリック操作とタップ操作が下にあるコントロールに渡されるようにします。
  • モーションの軽減をオーバーライド: モーションの軽減をリクエストしているユーザーに対して、スムーズな遷移補間を無効にします。
/* MANDATORY Copy-Paste Safety: Disable transition scaling for motion-sensitive users */
@media (prefers-reduced-motion: reduce) {
  .reveal-layer {
    transition: none !important;
  }
}

代替戦略

未登録のプロパティのフォールバック

mask-image をサポートしているが @property をサポートしていないブラウザでは、スポットライトは表示されますが、radial-gradient 内の値を補間できないため、動きがオンとオフの状態の間でジャンプします。var() を使用する場合は、フォールバック値を指定します。

.reveal-layer {
  mask-image: radial-gradient(
    circle at var(--mouse-x) var(--mouse-y),
    /* Use fallback values when using the `var()` function for browsers that don't get an initial value from the @property registration. */
    black var(--inner-size, 0%),
    transparent var(--outer-size, 0%)
  );
}

基本的なマスクのサポート

CSS マスキングをまったくサポートしていないブラウザの場合:

  1. 接頭辞付きプロパティ: より幅広いブラウザをサポートするには、-webkit-mask-image 接頭辞付きプロパティを使用します。
  2. プログレッシブ エンハンスメント: 表示効果がなくても、UI の基本状態が完全に機能し、読みやすくなるように設計します。これは、エフェクトが視覚的な効果を追加するだけで、コンテンツの読み取りに必要でない場合に便利です。