Анимированный эффект отслеживания мыши

Вот код представляющий элегантную реализацию эффекта отслеживания курсора мыши с учетом современных требований доступности и производительности. Разберем каждую часть кода подробно.

HTML структура

HTML
<!-- Заголовок демонстрации -->
<h1>Mouse Tracking Animation</h1>

<!-- Кнопка для демонстрации взаимодействия с эффектом -->
<a href="#" class="btn">Hover Me</a>

<!-- Блок для отображения координат курсора в реальном времени -->
<p id="coordinates">
  Coordinates<br>
  <strong></strong> <!-- Здесь будут отображаться x и y координаты -->
</p>

<!-- Элемент-указатель, который будет следовать за курсором -->
<!-- aria-hidden="true" скрывает элемент от экранных читалок,
     так как он носит чисто декоративный характер -->
<div class="pointer" aria-hidden="true"></div>

CSS стилизация

Базовые стили и фон

CSS
/* Сброс всех отступов и установка корректной модели блоков */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box; /* Включает padding и border в общую ширину */
}

body {
  font-family: system-ui, sans-serif; /* Использует системный шрифт */
  font-size: 1rem;
  line-height: 1.7; /* Улучшает читаемость текста */
  color: #070707;
  
  /* Создание сложного градиентного фона с сеточным паттерном */
  background: 
    /* Градиент от прозрачного к белому */
    fixed linear-gradient(transparent, #fff 70%),
    
    /* Вертикальные линии сетки каждые 40px */
    fixed repeating-linear-gradient(
      #efefef,
      #efefef 1px,
      transparent 1px,
      transparent 40px
    ),
    
    /* Горизонтальные линии сетки каждые 40px */
    fixed repeating-linear-gradient(
      to right,
      #efefef,
      #efefef 1px,
      transparent 1px,
      transparent 40px
    )
    #fff; /* Базовый белый фон */
  
  padding: 0 2rem;
  text-align: center;
  height: 100svh; /* Новая единица измерения - 100% высоты экрана */
  
  /* Flexbox для центрирования контента */
  display: flex;
  justify-content: center;
  align-items: center;
  flex-flow: column;
  gap: 1.5rem;
}

Стили заголовка

CSS
h1 {
  /* Адаптивный размер шрифта с помощью clamp() */
  font-size: clamp(2.5rem, 2vw + 2rem, 6rem);
  line-height: 1.3;
  font-weight: 900; /* Максимальная жирность */
  text-transform: uppercase; /* Заглавные буквы */
}

Стили кнопки

CSS
.btn {
  display: inline-block;
  padding: 1rem 4rem;
  font-size: 1.25rem;
  font-weight: 700;
  color: currentColor; /* Наследует цвет от родителя */
  text-align: center;
  text-decoration: none;
  border: 1px solid #ff8059; /* Оранжевая граница */
  border-radius: 0.5rem;
  outline: none;
  
  /* Плавные переходы для цвета и фона */
  transition: 0.5s ease-out;
  transition-property: color, background-color;
}

/* Эффекты при наведении и фокусе */
.btn:hover,
.btn:focus-visible {
  color: #fff; /* Белый текст */
  background-color: #ff8059; /* Оранжевый фон */
}

Главный элемент — указатель

CSS
.pointer {
  position: fixed; /* Фиксированное позиционирование относительно окна */
  top: 0;
  left: 0;
  z-index: 999; /* Высокий z-index для отображения поверх всего */
  width: 2rem;
  aspect-ratio: 1; /* Квадратная форма (ширина = высоте) */
  background-color: rgb(0 0 0 / 25%); /* Полупрозрачный черный фон */
  border: 1px solid transparent;
  border-radius: 100%; /* Круглая форма */
  pointer-events: none; /* Не блокирует клики мышью */
  
  /* Ключевая часть: использование CSS-переменных для позиционирования */
  transform: translate3d(var(--mouseX, 0), var(--mouseY, 0), 0)
    translate(-50%, -50%);
  
  /* Плавные переходы для всех изменений */
  transition: 0.3s ease-out;
  transition-property: transform, width, border, background-color;
}

/* Эффект при наведении на кнопку - указатель увеличивается */
body:has(.btn:hover) .pointer {
  width: 5rem; /* Увеличенный размер */
  background-color: transparent; /* Прозрачный фон */
  border-color: rgb(0 0 0 / 50%); /* Видимая граница */
}

Медиа-запросы для доступности

CSS
/* Скрытие анимации для пользователей с настройкой "уменьшить движение" */
@media (prefers-reduced-motion: reduce) {
  .pointer,
  #coordinates {
    display: none; /* Полностью скрываем элементы */
  }
}

/* Скрытие для сенсорных устройств (телефоны, планшеты) */
@media (pointer: coarse) {
  .pointer,
  #coordinates {
    display: none;
  }
}

JavaScript логика

JavaScript
// Проверка настроек пользователя на уменьшение анимации
const isReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)")
  .matches;

// Проверка на сенсорное устройство
const isTouch = window.matchMedia("(pointer: coarse)").matches;

// Определяем, должна ли запускаться анимация
// Анимация работает только на не-сенсорных устройствах 
// и когда пользователь не запрашивал уменьшение движения
const shouldRun = !isReducedMotion && !isTouch;

if (shouldRun) {
  // Переменные для хранения координат мыши
  let mouseX = 0;
  let mouseY = 0;
  
  // Получение ссылок на DOM элементы
  const pointer = document.querySelector(".pointer");
  const coordinates = document.querySelector("#coordinates strong");
  
  // Обработчик события движения мыши
  window.addEventListener("mousemove", (e) => {
    // Сохраняем координаты курсора относительно окна браузера
    mouseX = e.clientX;
    mouseY = e.clientY;
    
    // Опциональное логирование для отладки
    // console.log(mouseX, mouseY);
    
    // Обновление отображения координат в DOM
    if (coordinates) {
      coordinates.innerText = `x: ${mouseX} y: ${mouseY}`;
    }
  });
  
  // Функция анимации, использующая requestAnimationFrame
  function animate() {
    // Установка CSS-переменных для позиционирования указателя
    // setProperty позволяет динамически изменять CSS custom properties
    pointer.style.setProperty("--mouseX", `${mouseX}px`);
    pointer.style.setProperty("--mouseY", `${mouseY}px`);
    
    // Запрос следующего кадра анимации
    // Это обеспечивает синхронизацию с частотой обновления экрана (обычно 60 FPS)
    requestAnimationFrame(animate);
  }
  
  // Запуск цикла анимации
  animate();
}

Ключевые технологии и концепции

1. CSS Custom Properties (переменные)

Использование --mouseX и --mouseY позволяет JavaScript динамически обновлять позицию элемента без прямого манипулирования DOM.

2. transform: translate3d()

Использование translate3d() вместо изменения top/left обеспечивает аппаратное ускорение и лучшую производительность.

3. requestAnimationFrame()

Этот метод синхронизирует анимацию с частотой обновления экрана, обеспечивая плавность и оптимизируя производительность.

4. Доступность (Accessibility)

  • prefers-reduced-motion — уважает настройки пользователя по уменьшению анимации
  • pointer: coarse — определяет сенсорные устройства
  • aria-hidden="true" — скрывает декоративный элемент от скринридеров

5. Производительность

  • pointer-events: none — предотвращает блокировку событий мыши
  • Использование shouldRun флага для предотвращения ненужных вычислений
  • Оптимизированная анимационная петля с requestAnimationFrame

Принципы работы

  1. Инициализация: Код проверяет поддержку устройства и предпочтения пользователя
  2. Отслеживание: Событие mousemove сохраняет координаты курсора
  3. Анимация: requestAnimationFrame обновляет CSS-переменные каждый кадр
  4. Отображение: CSS использует переменные для плавного перемещения указателя
  5. Интерактивность: При наведении на кнопку указатель изменяет размер и стиль

Этот код демонстрирует современный подход к веб-анимации с учетом производительности, доступности и пользовательского опыта.

Позаимствовано отсюда: YouTube

CodePen лежит по этой ссылке