Интерактивное раскрытие контента

Добавьте на сайт эффективные интерактивные эффекты раскрытия с помощью масок 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. Прогрессивное улучшение. Разработайте базовое состояние интерфейса так, чтобы он был полностью функциональным и понятным без эффекта раскрытия. Это полезно, когда эффект добавляет только визуальный стиль и не является обязательным для чтения контента.