Клієнт попросив додати на сайт плавну анімацію перемикання іконок: коло перетворюється на зірку, зірка — на серце. Візуал готовий, але SVG-морфінг «з коробки» не працює — фігури смикаються, форма ламається. Виявилося, що початкова та кінцева фігури мають різну кількість вузлів. Ми вирішили це завдання за допомогою GSAP MorphSVGPlugin — GreenSock. Розповідаю, як налаштувати морфінг без головного болю та заощадити бюджет на налагодженні. За 5 років ми реалізували понад 50 проектів з SVG-анімацією, і морфінг — одне з найчастіших завдань. Нижче — перевірені інструменти, готові снипети та типові граблі.
Проблеми, які вирішуємо
Основна складність — незбіг кількості точок у path. SMIL та CSS-clip працюють лише при ідентичній структурі шляхів. Друга проблема — продуктивність на старих пристроях: анімація просідає нижче 30 FPS. Третя — складність інтеграції в сучасні фреймворки, особливо з SSR/SSG, де анімації на стороні клієнта потребують обережного підходу. Наприклад, в одному проекті ми зіткнулися з морфінгом логотипів у каталозі — неоптимізовані шляхи давали ривки на iPhone 8. Рішення: перевели все на GSAP з MorphSVG, і конверсія в картку товару зросла на 15%.
Як вибрати інструмент для морфінгу?
| Інструмент | Складність | Ціна | Продуктивність | Вирівнювання точок |
|---|---|---|---|---|
| GSAP MorphSVG | Середня | Платний | Висока | Автоматичне |
| Flubber.js | Низька | Безкоштовний | Середня | Тільки ручне |
| SMIL | Висока | Безкоштовний | Низька | Вимагає ідентичних шляхів |
Розрахуйте вартість вашого проекту — зв'яжіться з нами.
Чому GSAP MorphSVG кращий за альтернативи?
GSAP MorphSVGPlugin у 3 рази швидший у налаштуванні для складних шляхів, ніж Flubber.js. Він автоматично вирівнює кількість вузлів і підтримує timeline для послідовностей. У проекті з каталогом товарів ми реалізували морфінг логотипів брендів за допомогою React 18 та GSAP. Анімація при наведенні збільшила конверсію в картку товару на 15%.
Реалізація на React з GSAP
Використовуємо React 18, Next.js 14, TypeScript. Для морфінгу — GSAP з MorphSVGPlugin. Приклад реалізації:
// lib/gsap-morph.ts import { gsap } from 'gsap' import { MorphSVGPlugin } from 'gsap/MorphSVGPlugin' gsap.registerPlugin(MorphSVGPlugin) export { gsap } // components/MorphingIcon.tsx import { useEffect, useRef, useState } from 'react' import { gsap } from '../lib/gsap-morph' const shapes = { circle: 'M 50,10 A 40,40 0 1,1 49.9,10', star: 'M50,5 L61,35 L95,35 L68,57 L79,91 L50,70 L21,91 L32,57 L5,35 L39,35 Z', heart: 'M50,85 C10,60 5,30 25,15 C35,7 45,10 50,18 C55,10 65,7 75,15 C95,30 90,60 50,85 Z', arrow: 'M 10,50 L 40,20 L 40,35 L 90,35 L 90,65 L 40,65 L 40,80 Z', } type ShapeKey = keyof typeof shapes export function MorphingIcon() { const pathRef = useRef<SVGPathElement>(null) const [current, setCurrent] = useState<ShapeKey>('circle') const morphTo = (target: ShapeKey) => { if (!pathRef.current || target === current) return gsap.to(pathRef.current, { morphSVG: { shape: shapes[target], origin: '50% 50%', type: 'rotational', }, duration: 0.8, ease: 'power2.inOut', }) setCurrent(target) } return ( <div> <svg viewBox="0 0 100 100" className="w-32 h-32"> <path ref={pathRef} d={shapes.circle} fill="none" stroke="#3b82f6" strokeWidth="2" /> </svg> <div className="flex gap-2 mt-4"> {(Object.keys(shapes) as ShapeKey[]).map(key => ( <button key={key} onClick={() => morphTo(key)} className={`px-3 py-1 text-sm rounded ${ current === key ? 'bg-blue-500 text-white' : 'bg-gray-100' }`} > {key} </button> ))} </div> </div> ) } Альтернативні інструменти: Flubber.js та SMIL
Для простих двостанів підходить Flubber.js. Він безкоштовний, але потребує ручного вирівнювання точок. Встановлення: npm install flubber. Приклад використання — в коді нижче.
// components/FlubberMorph.tsx import { useEffect, useRef, useState } from 'react' import { interpolate } from 'flubber' export function FlubberMorph() { const pathRef = useRef<SVGPathElement>(null) const animFrameRef = useRef<number | null>(null) const morphBetween = ( startPath: string, endPath: string, durationMs = 800 ) => { const interpolator = interpolate(startPath, endPath, { maxSegmentLength: 2 }) const start = performance.now() const animate = (now: number) => { const elapsed = now - start const t = Math.min(elapsed / durationMs, 1) const ease = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t if (pathRef.current) { pathRef.current.setAttribute('d', interpolator(ease)) } if (t < 1) { animFrameRef.current = requestAnimationFrame(animate) } } if (animFrameRef.current) cancelAnimationFrame(animFrameRef.current) animFrameRef.current = requestAnimationFrame(animate) } const squarePath = 'M 20,20 L 80,20 L 80,80 L 20,80 Z' const blobPath = 'M 50,10 C 80,10 90,30 90,50 C 90,70 70,90 50,90 C 30,90 10,70 10,50 C 10,30 20,10 50,10 Z' const [isBlob, setIsBlob] = useState(false) useEffect(() => { if (pathRef.current) { pathRef.current.setAttribute('d', squarePath) } }, []) const toggle = () => { morphBetween( isBlob ? blobPath : squarePath, isBlob ? squarePath : blobPath ) setIsBlob(!isBlob) } return ( <div> <svg viewBox="0 0 100 100" className="w-48 h-48"> <path ref={pathRef} fill="#8b5cf6" /> </svg> <button onClick={toggle} className="mt-4 px-4 py-2 bg-purple-500 text-white rounded"> Морфінг </button> </div> ) } Якщо обидві фігури мають однакову кількість точок — можна використовувати SMIL без бібліотек. Приклад — квадрат перетворюється на ромб і назад. Детальніше читайте в документації MDN.
export function SMILMorph() { return ( <svg viewBox="0 0 100 100" className="w-32 h-32"> <path fill="#f59e0b"> <animate attributeName="d" dur="1.5s" repeatCount="indefinite" values=" M 20,20 L 80,20 L 80,80 L 20,80 Z; M 50,10 L 90,50 L 50,90 L 10,50 Z; M 20,20 L 80,20 L 80,80 L 20,80 Z " keyTimes="0; 0.5; 1" calcMode="spline" keySplines="0.4 0 0.2 1; 0.4 0 0.2 1" /> </path> </svg> ) } Як підготувати SVG-шляхи для морфінгу?
Для успішного морфінгу фігури мають бути оптимізовані. Використовуйте інструменти:
- SVGO — мінімізація та нормалізація шляхів.
- Inkscape → Розширення > Змінити шлях > Додати вузли — вирівнювання кількості точок.
-
GSAP MorphSVGPlugin.convertToPath() — перетворює
<circle>,<rect>тощо на<path>.
import { MorphSVGPlugin } from 'gsap/MorphSVGPlugin' MorphSVGPlugin.convertToPath('#my-circle, #my-rect') Додаткові рекомендації з підготовки
Перед морфінгом перевірте кількість вузлів через `path.getTotalLength()`. Різниця має бути мінімальною. Для складних фігур використовуйте GSAP — він сам вирівняє вузли. Якщо бюджет обмежений, виберіть Flubber.js і підготуйте шляхи вручну.Процес роботи та терміни
- Аналіз — узгоджуємо дизайн і перевіряємо вихідні фігури.
- Оптимізація — чистимо шляхи через SVGO, вирівнюємо вузли.
- Вибір бібліотеки — рекомендуємо GSAP для складних проектів.
- Реалізація — пишемо анімацію, тестуємо на мобільних пристроях.
- Інтеграція — вбудовуємо в додаток, перевіряємо Core Web Vitals (LCP, CLS).
| Тип роботи | Термін |
|---|---|
| Простий двостанів морфінг | від 4 годин |
| Інтерактивний морфінг з вибором фігур | 1–2 дні |
| Складна послідовність з кольором та інтерактивом | до 5 днів |
У вартість входить документація, доступ до репозиторію та демо, навчання команди замовника, підтримка 30 днів. Гарантуємо плавність 60 FPS на всіх пристроях. Замовте морфінг для вашого сайту — отримайте консультацію та оцінку проекту за 1 день. Зв'яжіться з нами.
Типові помилки
- Незбіг кількості вузлів — перевіряйте через
path.getTotalLength(). Навіть 1 зайвий вузол ламає анімацію. - Ігнорування ease-функцій — без них анімація виглядає неприродно. Використовуйте
power2.inOutабоback.out. - Відсутність обробки resize вікна — фігури можуть зміщуватися, якщо не перераховувати координати. Прив'яжіть анімацію до ресайзу через ResizeObserver.
Ми гарантуємо, що з нашим досвідом (5+ років, 50+ проектів) ваш морфінг працюватиме без ривків. Зв'яжіться з нами для консультації.







