互動式內容揭露

使用 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 的基本狀態,確保在沒有揭露效果的情況下,仍能正常運作且易於閱讀。如果效果只是增加視覺效果,而非閱讀內容的必要條件,這項功能就非常實用。