Системная настройка операционной среды, ограничивающая анимации, определяется через CSS Media Query:
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Этот механизм сигнализирует, что пользователь предпочитает минимальную динамику интерфейса. Причины могут быть связаны с вестибулярной чувствительностью, когнитивной перегрузкой или снижением дискомфорта при длительной работе с интерфейсами.
В JavaScript доступ к этому состоянию осуществляется через
matchMedia:
const reducedMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
const isReducedMotion = reducedMotionQuery.matches;
Изменения предпочтения могут происходить динамически:
reducedMotionQuery.addEventListener('change', (event) => {
console.log(event.matches ? 'reduced' : 'no-preference');
});
Библиотека Motion One предоставляет встроенную поддержку системного
параметра уменьшенной анимации. Основной механизм управляется параметром
reduceMotion.
import { animate } from "@motionone/dom";
animate(
".box",
{ transform: ["translateX(0px)", "translateX(200px)"] },
{ duration: 1, reduceMotion: "user" }
);
Значение reduceMotion определяет стратегию
поведения:
user — учитывается системная настройка
prefers-reduced-motionalways — анимации полностью отключаются независимо от
системыnever — анимации выполняются всегдаПоведение при user:
prefers-reduced-motion: reduce анимации сокращаются
до финального состоянияПолное удаление анимаций часто ухудшает восприятие интерфейса. Более устойчивый подход — замена движения на изменение прозрачности или состояния:
animate(
".modal",
{ opacity: [0, 1] },
{ duration: 0.2, reduceMotion: "user" }
);
При активированном reduced motion библиотека автоматически подавляет трансформации и сохраняет минимальную визуальную обратную связь.
При масштабных интерфейсах целесообразно использовать единый слой абстракции над Motion One.
import { animate } from "@motionone/dom";
const prefersReducedMotion =
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
function safeAnimate(element, keyframes, options = {}) {
return animate(element, keyframes, {
reduceMotion: "user",
...options,
duration: prefersReducedMotion ? 0 : options.duration
});
}
Такой слой позволяет унифицировать поведение всех анимаций без повторения логики в каждом вызове.
Timeline в Motion One агрегирует последовательные анимации:
import { timeline } from "@motionone/dom";
timeline([
[".a", { opacity: [0, 1] }, { duration: 0.3 }],
[".b", { transform: ["translateY(20px)", "translateY(0px)"] }, { duration: 0.5 }]
], {
reduceMotion: "user"
});
При активированном reduced motion последовательность сохраняется, но визуальное движение сводится к конечным состояниям, минимизируя когнитивную нагрузку.
Конфигурация reduceMotion может вступать в конфликт с
локальными решениями. Приоритет определяется следующим образом:
always — отключает анимации
полностьюprefers-reduced-motionnever — принудительное выполнение
анимацийЭто позволяет строить предсказуемую модель поведения:
animate(".card", { opacity: [0, 1] }, { reduceMotion: "always" });
При работе с составными интерфейсами важно учитывать не только отдельные анимации, но и их взаимодействие:
Параллакс при reduced motion требует полной замены на статическое позиционирование:
animate(
".background",
{ transform: ["translateY(0px)", "translateY(-100px)"] },
{ duration: 2, reduceMotion: "user" }
);
В режиме reduced motion фактическое смещение подавляется, предотвращая эффект движения фона.
Анимации, завязанные на пользовательские действия, также должны учитывать системные ограничения:
button.addEventListener("click", () => {
animate(".panel", { opacity: [0, 1], scale: [0.95, 1] }, {
duration: 0.25,
reduceMotion: "user"
});
});
При активированном reduced motion визуальный переход упрощается до мгновенного состояния без промежуточных фаз.
В крупных приложениях предпочтительно выделять слой управления motion-логикой:
export const motionConfig = {
reduceMotion: "user",
baseDuration: 0.4
};
Использование конфигурации:
import { animate } from "@motionone/dom";
import { motionConfig } from "./motionConfig";
function transition(element, props) {
return animate(element, props, {
reduceMotion: motionConfig.reduceMotion,
duration: motionConfig.baseDuration
});
}
Такой подход позволяет централизовать поведение и обеспечивает согласованность интерфейса.
Наиболее стабильная модель при reduced motion — отказ от промежуточной динамики в пользу дискретных состояний интерфейса:
Motion One автоматически поддерживает такой паттерн при использовании
reduceMotion: "user", но архитектурно важно проектировать
анимации так, чтобы финальное состояние было самостоятельным и
корректным без переходов.