Библиотека mo.js предоставляет возможность создавать не только стандартные фигуры (круги, прямоугольники, линии), но и полностью кастомные формы. Это ключевой механизм для построения сложных анимаций, логотипов, и уникальных визуальных эффектов.
Пользовательские формы реализуются через расширение базового класса
mojs.CustomShape, который позволяет определить собственную
SVG-геометрию.
В основе пользовательской формы лежит метод getShape(),
который должен возвращать строку SVG-разметки. Именно она используется
для отрисовки фигуры.
const MyShape = mojs.CustomShape({
getShape () {
return '<path d="M0,0 L100,0 L50,100 Z" />';
}
});
После определения форма регистрируется под уникальным именем:
mojs.addShape('myShape', MyShape);
Теперь её можно использовать как обычную фигуру:
new mojs.Shape({
shape: 'myShape',
fill: 'red'
});
Метод getShape() возвращает строку, содержащую
SVG-элемент. Чаще всего используются:
<path> — для сложных форм<circle> — круги<rect> — прямоугольники<polygon> — многоугольникиПример с polygon:
const StarShape = mojs.CustomShape({
getShape () {
return '<polygon points="50,0 61,35 98,35 68,57 79,91 50,70 21,91 32,57 2,35 39,35" />';
}
});
mo.js использует собственную систему координат внутри SVG. По умолчанию:
(0, 0)radiusВажно учитывать, что все координаты в SVG должны быть нормализованы относительно центра.
Пример неправильной формы:
return '<rect x="0" y="0" width="100" height="100" />';
Такая фигура будет смещена. Правильный вариант:
return '<rect x="-50" y="-50" width="100" height="100" />';
Размер пользовательской фигуры контролируется через параметр
radius, как и у стандартных фигур.
new mojs.Shape({
shape: 'myShape',
radius: 50
});
Важно:
getShape() должен быть рассчитан примерно на
диапазон [-50, 50]radiusДопускается использование SVG-трансформаций:
const RotatedShape = mojs.CustomShape({
getShape () {
return '<rect x="-50" y="-50" width="100" height="100" transform="rotate(45)" />';
}
});
Однако чаще рекомендуется управлять трансформациями через mo.js:
new mojs.Shape({
shape: 'rotatedShape',
angle: { 0: 360 }
});
Это позволяет синхронизировать анимацию с таймлайном.
mo.js автоматически создает SVG-контейнер, но иногда требуется явно
задать viewBox. Это делается через статические
свойства:
const Custom = mojs.CustomShape({
getShape () {
return '<path d="M0 0 L100 0 L50 100 Z" />';
},
viewBox: '0 0 100 100'
});
Если viewBox не задан, mo.js пытается вычислить его
автоматически, что может привести к некорректному масштабированию.
После регистрации кастомная форма полностью поддерживает систему анимации mo.js:
new mojs.Shape({
shape: 'myShape',
fill: 'none',
stroke: 'blue',
strokeWidth: { 0: 10 },
scale: { 0: 1 },
duration: 1000
});
Поддерживаются:
scalerotate (angle)opacitystrokeDashoffsetfill, strokeДля сложных форм часто используется анимация обводки:
new mojs.Shape({
shape: 'myShape',
fill: 'none',
stroke: 'black',
strokeWidth: 2,
strokeDasharray: '100%',
strokeDashoffset: { '100%': '0%' }
});
Это создает эффект “рисования” формы.
Внутри getShape() можно возвращать сразу несколько
SVG-элементов:
const ComplexShape = mojs.CustomShape({
getShape () {
return `
<g>
<circle cx="0" cy="0" r="30" />
<rect x="-10" y="-10" width="20" height="20" />
</g>
`;
}
});
Группа <g> позволяет объединять элементы в одну
форму.
Форма может зависеть от параметров:
const DynamicShape = mojs.CustomShape({
getShape () {
const size = 50;
return `<rect x="-${size/2}" y="-${size/2}" width="${size}" height="${size}" />`;
}
});
Однако стоит учитывать, что getShape() вызывается один
раз при создании.
Для создания сложных форм применяется <path> с
кривыми:
const CurveShape = mojs.CustomShape({
getShape () {
return `
<path d="
M -50 0
Q 0 -50 50 0
T 150 0
" />
`;
}
});
Это позволяет создавать плавные органические формы.
При работе с пользовательскими формами важно учитывать:
1. Минимизация сложности SVG
path2. Повторное использование форм
mojs.addShape3. Ограничение количества элементов
const ClippedShape = mojs.CustomShape({
getShape () {
return `
<svg>
<defs>
<clipPath id="clip">
<circle cx="0" cy="0" r="30" />
</clipPath>
</defs>
<rect x="-50" y="-50" width="100" height="100" clip-path="url(#clip)" />
</svg>
`;
}
});
const MaskShape = mojs.CustomShape({
getShape () {
return `
<svg>
<mask id="mask">
<rect x="-50" y="-50" width="100" height="100" fill="white"/>
<circle cx="0" cy="0" r="20" fill="black"/>
</mask>
<rect x="-50" y="-50" width="100" height="100" mask="url(#mask)" />
</svg>
`;
}
});
Пользовательские формы полностью совместимы с
mojs.Timeline:
const shape = new mojs.Shape({
shape: 'myShape',
scale: { 0: 1 }
});
const timeline = new mojs.Timeline();
timeline.add(shape);
timeline.play();
Смещение фигуры
Неправильный масштаб
viewBoxФорма не отображается
Анимация не применяется
const Arrow = mojs.CustomShape({
getShape () {
return `
<path d="
M -50 0
L 20 0
L 20 -20
L 50 0
L 20 20
L 20 0
" />
`;
}
});
mojs.addShape('arrow', Arrow);
new mojs.Shape({
shape: 'arrow',
stroke: 'black',
fill: 'none',
strokeWidth: 4,
scale: { 0: 1 },
angle: { 0: 360 }
});
const Star = mojs.CustomShape({
getShape () {
return '<polygon points="50,0 61,35 98,35 68,57 79,91 50,70 21,91 32,57 2,35 39,35" />';
}
});
mojs.addShape('star', Star);
new mojs.Shape({
shape: 'star',
fill: 'yellow',
scale: { 0.5: 1.2 },
yoyo: true,
repeat: 999
});
CustomShape — фабрика, возвращающая классaddShape — регистрация в глобальном реестреShape — универсальный контейнер для анимацииТакой подход позволяет отделить описание геометрии от логики анимации, обеспечивая переиспользуемость и масштабируемость кода.