В адаптивных интерфейсах анимации в mo.js должны подстраиваться под размер экрана, плотность пикселей и особенности устройства так же гибко, как и сам UI. Жёстко заданные координаты, фиксированные радиусы и неизменяемые тайминги приводят к визуальным ошибкам: элементы выходят за пределы экрана, траектории становятся непропорциональными, эффект теряет выразительность на мобильных устройствах.
Базовый подход к адаптивности заключается в вычислении параметров анимации относительно размеров окна браузера.
const vw = window.innerWidth;
const vh = window.innerHeight;
const radius = Math.min(vw, vh) * 0.1;
const burst = new mojs.Burst({
radius: { 0: radius * 2 },
count: 10,
children: {
radius: radius / 4,
fill: 'white'
}
});
document.addEventListener('click', (e) => {
burst
.tune({ x: e.pageX, y: e.pageY })
.replay();
});
Здесь ключевой принцип заключается в том, что геометрия эффекта зависит от viewport, а не от фиксированных чисел. Это позволяет сохранять пропорции на экранах разных размеров.
Адаптивные анимации требуют постоянного пересчёта конфигурации при изменении размеров окна.
let vw = window.innerWidth;
let vh = window.innerHeight;
const updateMetrics = () => {
vw = window.innerWidth;
vh = window.innerHeight;
};
window.addEventListener('resize', updateMetrics);
Использование централизованного пересчёта позволяет избежать рассинхронизации между визуальным состоянием и реальными размерами контейнера.
Линейные и криволинейные движения в mo.js часто задаются через абсолютные значения. Для адаптивности они должны быть выражены через относительные коэффициенты.
const shiftX = () => window.innerWidth * 0.2;
const shiftY = () => window.innerHeight * 0.15;
const tween = new mojs.Tween({
duration: 800,
onUpdate: (progress) => {
const x = shiftX() * progress;
const y = shiftY() * progress;
element.style.transform = `translate(${x}px, ${y}px)`;
}
});
Такой подход делает траекторию независимой от разрешения экрана и сохраняет визуальную динамику.
На устройствах с высокой плотностью пикселей (Retina и аналоги)
одинаковые значения радиусов и толщин выглядят визуально меньше.
Корректировка через devicePixelRatio позволяет
компенсировать этот эффект.
const dpr = window.devicePixelRatio || 1;
const circle = new mojs.Shape({
radius: 50 * dpr,
strokeWidth: 2 * dpr,
fill: 'transparent',
stroke: '#4dd0e1'
});
При этом важно избегать прямолинейного масштабирования всех параметров, так как чрезмерное увеличение может привести к перегрузке сцены.
Burst-анимации особенно чувствительны к размерам экрана, так как количество частиц и их радиус должны соответствовать доступному пространству.
const computeCount = () => Math.floor(window.innerWidth / 50);
const burst = new mojs.Burst({
count: computeCount(),
radius: { 0: 120 },
children: {
shape: 'circle',
radius: { 6: 0 },
duration: 1000
}
});
Число элементов увеличивается на больших экранах и уменьшается на малых, сохраняя визуальный баланс.
В сложных интерфейсах недостаточно просто обновлять переменные — анимации необходимо пересоздавать при изменении условий среды.
let burst;
const createBurst = () => {
const radius = Math.min(window.innerWidth, window.innerHeight) * 0.15;
burst = new mojs.Burst({
radius: { 0: radius },
count: 12,
children: {
radius: radius / 5,
fill: '#ff4081'
}
});
};
createBurst();
window.addEventListener('resize', () => {
createBurst();
});
Такой подход особенно важен при значительных изменениях layout, например при переходе между ориентациями устройства.
Не только геометрия, но и временные характеристики должны изменяться в зависимости от контекста. На мобильных устройствах часто требуется ускорение анимаций.
const baseDuration = 600;
const getDuration = () => {
return window.innerWidth < 600 ? baseDuration * 0.7 : baseDuration;
};
const tween = new mojs.Tween({
duration: getDuration(),
easing: 'cubic.out'
});
Это позволяет поддерживать ощущение отзывчивости интерфейса независимо от мощности устройства.
Абсолютные координаты быстро теряют актуальность в адаптивной среде. Более устойчивый подход — использование относительных позиций от центра или краёв экрана.
const centerX = () => window.innerWidth / 2;
const centerY = () => window.innerHeight / 2;
const shape = new mojs.Shape({
x: centerX,
y: centerY,
radius: 40,
fill: '#29b6f6'
});
При изменении размеров окна центр автоматически пересчитывается без дополнительных действий.
В современных интерфейсах анимации часто зависят от DOM-структуры.
Использование getBoundingClientRect позволяет
синхронизировать mo.js с реальными элементами.
const button = document.querySelector('.button');
button.addEventListener('click', () => {
const rect = button.getBoundingClientRect();
const burst = new mojs.Burst({
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2,
radius: { 0: rect.width },
count: 8
});
burst.replay();
});
Такой подход обеспечивает точное позиционирование эффектов относительно интерфейсных компонентов.
Частые пересчёты параметров при resize могут стать узким местом производительности. Оптимизация достигается через throttling событий и кеширование вычислений.
let timeout;
window.addEventListener('resize', () => {
clearTimeout(timeout);
timeout = setTimeout(() => {
updateMetrics();
}, 150);
});
Дополнительно полезно избегать пересоздания анимаций без
необходимости, ограничиваясь обновлением параметров через
tune().
Адаптивность включает не только размеры, но и контекст использования. Один и тот же эффект может иметь разные конфигурации для мобильного и десктопного режима.
const isMobile = () => window.innerWidth < 768;
const burstConfig = {
count: isMobile() ? 6 : 14,
radius: isMobile() ? 80 : 160,
children: {
radius: isMobile() ? 4 : 8
}
};
const burst = new mojs.Burst(burstConfig);