Модульная структура — ключевой фактор поддерживаемости и масштабируемости анимационных проектов на базе библиотеки mo.js. Разделение кода на логические части позволяет изолировать ответственность, упростить повторное использование анимаций и снизить связанность компонентов.
Основной подход заключается в разделении проекта на следующие уровни:
Каждый уровень реализуется в виде отдельных модулей (файлов или директорий), что соответствует принципам ES Modules.
Пример типичной структуры проекта:
/src
/animations
burst.js
shape.js
timeline.js
/components
buttonAnimation.js
loaderAnimation.js
/presets
colors.js
easing.js
/utils
helpers.js
math.js
index.js
Каждый тип анимации выносится в отдельный модуль. Это позволяет переиспользовать логику и легко изменять поведение.
// /animations/burst.js
import mojs from 'mo-js';
export function createBurst(options = {}) {
return new mojs.Burst({
radius: { 0: 100 },
count: 10,
children: {
shape: 'circle',
fill: 'cyan',
duration: 1500
},
...options
});
}
Особенности:
optionsКомпоненты объединяют несколько анимаций в единое поведение.
// /components/buttonAnimation.js
import { createBurst } from '../animations/burst.js';
export function animateButton(el) {
const burst = createBurst({
parent: el,
left: 0,
top: 0
});
el.addEventListener('click', () => {
burst.replay();
});
}
Ключевые аспекты:
Сложные анимации удобно инкапсулировать в Timeline.
// /animations/timeline.js
import mojs from 'mo-js';
export function createTimeline(animations) {
const timeline = new mojs.Timeline();
animations.forEach(anim => timeline.add(anim));
return timeline;
}
Применение:
import { createBurst } from './burst.js';
import { createTimeline } from './timeline.js';
const burst1 = createBurst({ x: 100 });
const burst2 = createBurst({ x: 200 });
const timeline = createTimeline([burst1, burst2]);
timeline.play();
Конфигурации выносятся в отдельные файлы для централизованного управления параметрами.
// /presets/colors.js
export const COLORS = {
primary: '#00FFFF',
secondary: '#FF00FF',
accent: '#FFD700'
};
// /presets/easing.js
import mojs from 'mo-js';
export const EASING = {
bounce: mojs.easing.bounce.out,
elastic: mojs.easing.elastic.out
};
Использование:
import { COLORS } from '../presets/colors.js';
fill: COLORS.primary
Утилиты помогают избежать дублирования кода.
// /utils/helpers.js
export function random(min, max) {
return Math.random() * (max - min) + min;
}
Использование:
import { random } from '../utils/helpers.js';
radius: { 0: random(50, 150) }
Главный файл связывает все модули.
// index.js
import { animateButton } from './components/buttonAnimation.js';
const button = document.querySelector('.btn');
animateButton(button);
Каждый файл выполняет строго определённую задачу:
Модули не должны напрямую зависеть друг от друга без необходимости.
Неправильно:
import { COLORS } from '../presets/colors.js';
import { animateButton } from '../components/buttonAnimation.js';
Правильно:
Все анимации должны поддерживать расширение через аргументы:
export function createShape(options = {}) {
return new mojs.Shape({
fill: 'red',
...options
});
}
Модуль должен быть пригоден для использования в разных местах:
const burst1 = createBurst({ x: 50 });
const burst2 = createBurst({ x: 150 });
Современная структура проекта предполагает использование ES Modules:
export function createBurst() {}
export const COLORS = {};
import { createBurst } from './animations/burst.js';
Для удобства можно создавать агрегирующие модули.
// /animations/index.js
export { createBurst } from './burst.js';
export { createTimeline } from './timeline.js';
Использование:
import { createBurst, createTimeline } from './animations/index.js';
Модули должны скрывать внутреннюю реализацию:
export function createLoader() {
const circle = new mojs.Shape({...});
const burst = new mojs.Burst({...});
return new mojs.Timeline().add(circle, burst);
}
Пользователь модуля работает только с результатом.
Для оптимизации можно использовать lazy-loading:
button.addEventListener('click', async () => {
const { animateButton } = await import('./components/buttonAnimation.js');
animateButton(button);
});
При росте проекта структура усложняется:
/src
/core
/animations
/basic
/complex
/components
/ui
/effects
/presets
/utils
createBurst, animateButtonburst.js, buttonAnimation.jsCOLORS, EASINGmo.js не работает напрямую с DOM-структурой, поэтому:
Пресеты позволяют стандартизировать анимации:
// /presets/burstPreset.js
export const BURST_PRESET = {
radius: { 0: 100 },
count: 12,
children: {
shape: 'circle',
fill: 'white'
}
};
Использование:
createBurst(BURST_PRESET);
Модульная структура хорошо сочетается с:
Позволяет:
Изолированные модули проще тестировать:
import { createBurst } from './burst.js';
test('creates burst animation', () => {
const burst = createBurst();
expect(burst).toBeDefined();
});
Модульная архитектура позволяет переносить:
в другие проекты без изменений.
При создании сложных интерфейсов:
Такой подход обеспечивает: