互动内容揭晓

使用 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. 渐进增强:将界面的基本状态设计为在没有揭示效果的情况下也能完全正常运行且清晰易读。当效果只是增加视觉吸引力,而不是阅读内容的要求时,这非常有用。