Медиазапросы в анимациях становятся ключевым механизмом адаптации motion-дизайна к различным устройствам и пользовательским предпочтениям. В связке с Popmotion они позволяют динамически изменять параметры анимации, полностью перестраивать сценарии движения и отключать сложные эффекты без пересборки логики.
Браузерные медиазапросы через window.matchMedia
представляют собой событийный API, который позволяет отслеживать
изменения условий среды: ширину экрана, плотность пикселей, предпочтения
пользователя.
В контексте анимаций медиазапрос перестаёт быть только инструментом CSS и превращается в триггер конфигурации поведения анимации.
const mq = window.matchMedia('(min-width: 900px)')
mq.addEventListener('change', (e) => {
if (e.matches) {
// десктопная конфигурация анимации
} else {
// мобильная конфигурация
}
})
Такой подход формирует реактивную модель: анимация перестраивается при изменении условий среды, а не только при инициализации компонента.
Popmotion предоставляет функцию animate, которая
управляет временем, физикой и интерполяцией значений. В связке с
медиазапросами она используется как конечный исполнитель конфигурации,
выбранной на основе состояния среды.
import { animate } from 'popmotion'
const mq = window.matchMedia('(min-width: 900px)')
function startAnimation(isDesktop) {
animate({
from: 0,
to: isDesktop ? 300 : 100,
duration: isDesktop ? 800 : 400,
onUpdate: (latest) => {
document.querySelector('.box').style.transform =
`translateX(${latest}px)`
}
})
}
startAnimation(mq.matches)
mq.addEventListener('change', (e) => {
startAnimation(e.matches)
})
Здесь медиазапрос управляет не самим движением, а параметрами движения: расстоянием, длительностью и характером анимации.
Popmotion поддерживает физические модели (spring, inertia), которые особенно чувствительны к устройству и контексту использования. На мобильных устройствах часто требуется более мягкая динамика, на десктопе — более выраженная.
import { animate, spring } from 'popmotion'
const mq = window.matchMedia('(min-width: 900px)')
function createSpring() {
return mq.matches
? { stiffness: 200, damping: 20 }
: { stiffness: 120, damping: 18 }
}
animate({
from: 0,
to: 1,
ease: spring(createSpring()),
onUpdate: (v) => {
document.querySelector('.card').style.transform =
`scale(${1 + v * 0.1})`
}
})
Физическая модель становится адаптивной, а не фиксированной. Это позволяет учитывать не только размеры экрана, но и характер взаимодействия.
Одним из наиболее значимых медиазапросов для анимаций является
prefers-reduced-motion. Он отражает системное предпочтение
пользователя минимизировать движение интерфейса.
Popmotion не требует специального режима для этого запроса — достаточно изменить параметры анимации или заменить её мгновенным состоянием.
const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)')
function runAnimation() {
if (reducedMotion.matches) {
document.querySelector('.panel').style.opacity = 1
document.querySelector('.panel').style.transform = 'none'
return
}
animate({
from: 0,
to: 1,
duration: 600,
onUpdate: (v) => {
const el = document.querySelector('.panel')
el.style.opacity = v
el.style.transform = `translateY(${20 * (1 - v)}px)`
}
})
}
runAnimation()
В этом сценарии медиазапрос полностью изменяет стратегию исполнения: вместо анимации применяется статическое состояние.
Реальные интерфейсы редко зависят от одного условия. Обычно учитывается комбинация факторов: ширина экрана, ориентация устройства и предпочтения движения.
const mqDesktop = window.matchMedia('(min-width: 900px)')
const mqLandscape = window.matchMedia('(orientation: landscape)')
const mqReduced = window.matchMedia('(prefers-reduced-motion: reduce)')
function getConfig() {
if (mqReduced.matches) {
return { duration: 0 }
}
if (mqDesktop.matches && mqLandscape.matches) {
return { duration: 900, distance: 300 }
}
return { duration: 500, distance: 150 }
}
function run() {
const config = getConfig()
animate({
from: 0,
to: config.distance,
duration: config.duration,
onUpdate: (v) => {
document.querySelector('.item').style.transform =
`translateX(${v}px)`
}
})
}
Так формируется декларативная модель конфигурации, где медиазапросы определяют параметры анимации, а Popmotion выполняет вычислительную часть.
Один из сложных аспектов — корректное обновление анимаций при изменении условий. Popmotion-анимации не всегда автоматически адаптируются к смене медиазапроса, поэтому требуется контроль жизненного цикла.
import { animate } from 'popmotion'
let currentAnimation = null
function start() {
if (currentAnimation) currentAnimation.stop()
const isWide = window.matchMedia('(min-width: 900px)').matches
currentAnimation = animate({
from: 0,
to: isWide ? 400 : 200,
duration: 700,
onUpdate: (v) => {
document.querySelector('.block').style.left = `${v}px`
}
})
}
start()
window.matchMedia('(min-width: 900px)')
.addEventListener('change', start)
Здесь важен контроль предыдущего процесса: без остановки анимации возникает конфликт обновлений состояния.
При усложнении интерфейса медиазапросы перестают влиять на отдельные параметры и начинают определять целые пресеты анимационного поведения.
const presets = {
mobile: {
duration: 300,
distance: 120,
easing: 'ease-out'
},
desktop: {
duration: 700,
distance: 320,
easing: 'ease-in-out'
}
}
function getPreset() {
return window.matchMedia('(min-width: 900px)').matches
? presets.desktop
: presets.mobile
}
animate({
from: 0,
to: getPreset().distance,
duration: getPreset().duration,
onUpdate: (v) => {
document.querySelector('.el').style.transform =
`translateX(${v}px)`
}
})
Такой подход упрощает масштабирование системы анимаций: изменения происходят на уровне конфигурации, а не логики движения.
В сложных интерфейсах Popmotion часто используется для управления группами анимаций. Медиазапросы позволяют синхронизировать их параметры, обеспечивая согласованное поведение.
function runGroup() {
const isDesktop = window.matchMedia('(min-width: 900px)').matches
animate({
from: 0,
to: isDesktop ? 200 : 80,
onUpdate: (v) => {
document.querySelector('.a').style.transform = `translateX(${v}px)`
document.querySelector('.b').style.transform = `translateY(${v / 2}px)`
document.querySelector('.c').style.opacity = v / 200
}
})
}
Единый источник конфигурации предотвращает рассинхронизацию анимационных компонентов.
Адаптация анимаций через медиазапросы позволяет не только изменять визуальное поведение, но и управлять нагрузкой. На слабых устройствах можно полностью отключать физические модели или уменьшать частоту обновления.
const isLowPower = window.matchMedia('(max-width: 480px)').matches
animate({
from: 0,
to: 1,
duration: isLowPower ? 200 : 800,
onUpdate: (v) => {
document.querySelector('.gpu').style.opacity = v
}
})
В более сложных системах этот механизм расширяется до выбора между requestAnimationFrame-ориентированными и таймерными стратегиями исполнения.
При масштабировании интерфейсов медиазапросы становятся частью архитектурного слоя, отвечающего за конфигурацию motion-системы. Popmotion в таком случае выступает исполнительным движком, а медиазапросы — слоем принятия решений.
const queries = {
desktop: window.matchMedia('(min-width: 1200px)'),
tablet: window.matchMedia('(min-width: 768px)')
}
function resolveMode() {
if (queries.desktop.matches) return 'desktop'
if (queries.tablet.matches) return 'tablet'
return 'mobile'
}
function animateLayout() {
const mode = resolveMode()
const config = {
desktop: { x: 400, duration: 900 },
tablet: { x: 250, duration: 600 },
mobile: { x: 120, duration: 350 }
}[mode]
animate({
from: 0,
to: config.x,
duration: config.duration,
onUpdate: (v) => {
document.querySelector('.layout').style.transform =
`translateX(${v}px)`
}
})
}
animateLayout()