Feature detection

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

Feature detection в этом контексте представляет собой набор проверок, позволяющих определить поддержку ключевых возможностей: CSS transforms, requestAnimationFrame, pointer events, passive listeners, аппаратное ускорение и предпочтения пользователя.


Роль feature detection в архитектуре анимации

Анимационные системы зависят от множества факторов среды исполнения. Один и тот же код может выполняться:

  • в современных браузерах с поддержкой GPU-ускорения
  • в старых средах без pointer events
  • в окружениях с ограниченным DOM API
  • в режимах повышенной производительности (low-end устройства)

Popmotion строит поведение анимации вокруг фактических возможностей, а не вокруг гипотетического стандарта.

Ключевая цель feature detection — избежать ветвления логики внутри анимационного цикла и перенести выбор стратегии на этап инициализации.


Проверка requestAnimationFrame

Одним из базовых элементов является наличие requestAnimationFrame. Это основной механизм синхронизации анимации с циклом рендеринга браузера.

Типовая проверка:

const raf = typeof requestAnimationFrame !== 'undefined'
  ? requestAnimationFrame
  : (callback) => setTimeout(callback, 16);

При отсутствии RAF Popmotion переключается на таймерный fallback. Это снижает точность анимации, но сохраняет функциональность.

Дополнительный уровень проверки может учитывать префиксные реализации:

const raf = window.requestAnimationFrame ||
  window.webkitRequestAnimationFrame ||
  window.mozRequestAnimationFrame;

Определение поддержки CSS transforms

Одним из ключевых оптимизационных путей Popmotion является использование CSS transforms вместо изменения layout-свойств.

Feature detection здесь направлена на определение:

  • поддержки transform
  • аппаратного ускорения через translate3d
  • корректной работы will-change

Базовая проверка может выглядеть как создание тестового элемента:

const testEl = document.createElement('div');

const supportsTransform = 'transform' in testEl.style ||
  'webkitTransform' in testEl.style ||
  'msTransform' in testEl.style;

Более продвинутый подход включает проверку GPU-ускорения:

const canAccelerate = (() => {
  const el = document.createElement('div');
  el.style.transform = 'translate3d(1px,1px,1px)';
  return el.style.transform.length > 0;
})();

Если трансформации недоступны или нестабильны, Popmotion может переключаться на изменение top/left, хотя это менее эффективно.


Passive event listeners

Одним из критических факторов производительности является возможность использования passive listeners, особенно для событий прокрутки и pointer input.

Passive listeners позволяют браузеру не блокировать основной поток при обработке событий.

Проверка:

let supportsPassive = false;

try {
  window.addEventListener('test', null, {
    get passive() {
      supportsPassive = true;
    }
  });
} catch (e) {}

Использование:

const eventOptions = supportsPassive
  ? { passive: true }
  : false;

В Popmotion это влияет на обработку drag и scroll взаимодействий, снижая задержки ввода.


Pointer Events и fallback на Mouse/Touch

Современные браузеры поддерживают Pointer Events, объединяющие мышь, касание и стилус в единый интерфейс.

Feature detection:

const hasPointerEvents = 'PointerEvent' in window;

При отсутствии поддержки используется комбинация:

  • mousedown / mousemove / mouseup
  • touchstart / touchmove / touchend

Абстракция Popmotion скрывает различия, но стратегия обработки событий выбирается на основании результата проверки.


Поддержка transform-origin и layout моделей

Некоторые оптимизации зависят от корректной поддержки CSS свойств:

  • transform-origin
  • position: fixed
  • flexbox (для корректного позиционирования анимируемых элементов)

Проверка выполняется через style-инъекции:

const el = document.createElement('div');

const supportsFlex = 'display' in el.style &&
  (() => {
    el.style.display = 'flex';
    return el.style.display === 'flex';
  })();

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


Detecting reduced motion preference

Системная настройка пользователя prefers-reduced-motion влияет на поведение анимации. Popmotion учитывает её для автоматического снижения интенсивности анимационных эффектов.

const prefersReducedMotion = window.matchMedia &&
  window.matchMedia('(prefers-reduced-motion: reduce)').matches;

При активном режиме:

  • уменьшается длительность tween
  • отключаются spring-анимации с высокой частотой колебаний
  • упрощаются easing-функции
  • сокращается количество промежуточных кадров

Определение поддержки DOM API

Popmotion активно использует DOM-методы для измерений и управления стилями. Feature detection включает проверку:

  • getBoundingClientRect
  • Element.prototype.animate (Web Animations API)
  • classList

Пример:

const supportsWAAPI = 'animate' in Element.prototype;

При наличии Web Animations API часть анимаций может делегироваться нативному движку браузера.


GPU acceleration heuristics

Отдельный слой проверки связан с оценкой возможности использования GPU-ускорения. Полноценного стандартизированного API для этого нет, поэтому используются эвристики:

  • наличие transform3d
  • отсутствие forced reflow при изменении transform
  • поведение will-change

Пример проверки:

const supportsGPU = (() => {
  const el = document.createElement('div');
  el.style.willChange = 'transform';
  return el.style.willChange === 'transform';
})();

Эти проверки не гарантируют фактическое использование GPU, но помогают выбрать наиболее безопасную стратегию.


Centralized feature map

В Popmotion feature detection обычно централизуется в объекте возможностей, чтобы избежать повторных вычислений:

const features = {
  raf: typeof requestAnimationFrame !== 'undefined',
  transform: 'transform' in document.body.style,
  pointer: 'PointerEvent' in window,
  passive: false,
  reducedMotion: window.matchMedia?.('(prefers-reduced-motion: reduce)').matches || false
};

Такой объект используется как источник истины при выборе:

  • типа анимации (tween vs spring)
  • способа обработки input
  • метода применения стилей
  • частоты обновления

Lazy evaluation стратегий

Особенность Popmotion заключается в отложенном принятии решений. Feature detection не всегда выполняется сразу при загрузке. В некоторых случаях проверка производится:

  • при первом создании animation instance
  • при первом pointer interaction
  • при первом DOM access

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


Edge-case поведение в старых окружениях

В старых браузерах или ограниченных средах feature detection становится критическим механизмом деградации функциональности:

  • отсутствие addEventListener → fallback на attachEvent
  • отсутствие transform → прямое изменение layout
  • отсутствие RAF → таймерный loop
  • отсутствие pointer events → разделение input потоков

Popmotion в таких условиях сохраняет поведение анимации ценой точности и плавности.


Приоритеты при выборе стратегии

Результаты feature detection не используются изолированно. Они комбинируются в приоритетную систему:

  1. Web Animations API (если доступен и стабилен)
  2. CSS transforms + RAF
  3. DOM layout manipulation + RAF
  4. Timer-based animation loop

Такая иерархия позволяет минимизировать нагрузку на main thread и максимально использовать возможности платформы.


Минимизация стоимости проверок

Feature detection в Popmotion проектируется с учётом:

  • кеширования результатов
  • однократного вычисления
  • отсутствия проверок внутри animation loop
  • отказа от дорогостоящих DOM операций в горячем пути

Это критично для высокочастотных анимаций, где даже микрозадержки влияют на стабильность FPS.