
✨ Анімація ліній і замкнутих контурів SVG на чистому CSS
SVG-анімація штрихів, один із тих прийомів, які перетворюють статичну іконку на живу ілюстрацію. Ви напевно бачили цей ефект: контур логотипу плавно промальовується, ніби невидиме перо обводить форму від початку до кінця. Виглядає дорого, а робиться на чистому CSS.
Проблема в тому, що більшість посібників зупиняються на найпростішому випадку: відкритий шлях, одна лінія, анімація від початку до кінця. Але реальна графіка — це замкнені контури, ламані форми та напрямки промальовування, які дизайнер не контролював. SVG-шлях може починатися будь-де, а вам потрібно, щоб він малювався за годинниковою стрілкою та з естетичної стартової точки.
Нижче, повний набір технік: від базової анімації до подвійних ліній і повного контролю над початковою та кінцевою точкою. Усе на чистому CSS, без бібліотек і рантайм-вимірювань. Методи випробувані під час розробки плагіна SVG Divider і працюють у будь-якому сучасному браузері.
💡 Швидкий огляд:
- Дізнайтеся довжину SVG-шляху через
getTotalLength()або Illustrator і пропишіть число в CSS. - Налаштуйте
stroke-dasharray: LENGTHта анімуйтеstroke-dashoffsetвідLENGTHдо0. - Для реверсу напрямку міняйте місцями штрих і пробіл у
stroke-dasharrayі штовхайтеstroke-dashoffsetуперед. - Змістіть початкову точку додаванням до
stroke-dashoffsetполовини (або довільної частки) довжини. - Запустіть анімацію «зсередини назовні»: старт від
HALF_LENGTH, фініш наFULL_LENGTH. - Налаштуйте подвійні лінії через половинний
stroke-dasharrayі чверть-зсувstroke-dashoffset.
Технічна база: штрихи, пробіли та довжина шляху
В основі будь-якої SVG-анімації лежать дві CSS-властивості: stroke-dasharray і stroke-dashoffset. Перша задає візерунок «штрих-пробіл» уздовж контуру, друга зсуває цей візерунок. Якщо зробити штрих рівним повній довжині шляху, а потім анімувати зсув, лінія «намалюється» на очах.
Ключовий крок перед будь-якою анімацією, дізнатися повну довжину шляху. Способи:
- JavaScript:
document.querySelector('path').getTotalLength(), один виклик у консолі, число підставляється в CSS жорстко. Жодного рантайму. - Illustrator: панель «Інформація про документ» показує довжину виділеного шляху.
- Власний скрипт: якщо шляхів десятки чи сотні, має сенс написати автоматичний обхід і розмітку довжин.
Важливий нюанс: getTotalLength() та Illustrator можуть дати трохи різні цифри для одного й того самого шляху. Причина, різна точність обчислень. Розбіжність зазвичай у межах 1-2 одиниць, на візуал не впливає.
Перевірте геометрію шляху на помилки. Напрямок малювання та початкова точка залежать від того, як дизайнер побудував криву в редакторі — це нормально. Нижче ми навчимося керувати і тим, і іншим без правки самого SVG.
Базова анімація: малюємо лінію від початку до кінця
Найпростіший випадок: відкритий шлях довжиною, скажімо, 100 одиниць. Ставимо штрих на всю довжину й анімуємо зміщення від 100 до 0:
1 @keyframes reveal { 2 to { 3 stroke-dashoffset: 0; 4 } 5 } 6 svg path { 7 stroke-dashoffset: 100; 8 stroke-dasharray: 100; 9 animation: reveal 1s ease-in-out 0s forwards; 10 }
Що тут відбувається: stroke-dasharray: 100 задає 100 одиниць суцільної лінії та 100 одиниць порожнечі (друге значення за замовчуванням дорівнює першому). stroke-dashoffset: 100 зсуває візерунок так, що видима частина виходить за межі шляху, лінія невидима. Анімація зводить зміщення до нуля, і штрих заповзає на місце.
Кілька практичних зауважень:
FULL_LENGTHзамініть на реальне число без одиниць вимірювання. CSS-рушій не розумієpxчиemусерединіstroke-dasharray.- Селектор
svg path, для одного контуру. Якщо в SVG кілька шляхів, використовуйте класи, ID або псевдокласи на кшталтpath:nth-of-type(3). Інструменти для налагодження::nth Testerі nthmaster.com. - Ім’я
@keyframesмає бути унікальним для кожного варіанту анімації. Різні шляхи часто потребують власних ключових кадрів. - Уникайте від’ємних значень
stroke-dashoffset: Safari при від’ємному зміщенні може блимати або показувати артефакти.
Керування напрямком: реверс промальовування
За замовчуванням анімація йде від початку шляху до кінця, туди, куди дизайнер провів криву. Але часто потрібно зворотне: щоб замкнений контур малювався за годинниковою стрілкою, а не проти.
Рішення, інвертувати візерунок штриха: лінія стартує невидимою (0 штриха, 100 пробілу) і стає видимою (100 штриха, 0 пробілу), а зміщення тягне її назад:
1 @keyframes reversed { 2 from { 3 stroke-dasharray: 0 100; 4 stroke-dashoffset: 0; 5 } 6 to { 7 stroke-dasharray: 100 0; 8 stroke-dashoffset: 100; 9 } 10 } 11 svg path { 12 animation: reversed 1s ease-in-out 0s forwards; 13 }
Механіка працює за рахунок циклічності штрихового візерунка: суцільна лінія матеріалізується на кінці шляху, а її копія йде вліво за початок і залишається невидимою. Те саме відбувається на замкнених контурах: копія штриха з’являється «перед» початком і не заважає сприйняттю.
Є альтернативний підхід, коротший і без маніпуляцій із масивом:
1 @keyframes alternative-reversed { 2 from { 3 stroke-dashoffset: 100; 4 } 5 to { 6 stroke-dashoffset: 200; 7 } 8 } 9 svg path { 10 stroke-dasharray: 100; 11 animation: alternative-reversed 1s ease-in-out 0s forwards; 12 }
Тут stroke-dasharray зафіксовано на повній довжині, а зміщення йде від 100 до 200. Результат той самий, лінія малюється у зворотному напрямку. Використовуйте варіант, який вам інтуїтивно зрозуміліший.
Зміщення початкової точки
Замкнений шлях схожий на кільцеву залізницю: потяг може стартувати з будь-якої станції. Завдання, зсунути початок анімації в естетичну точку, не чіпаючи саму геометрію.
Старт із протилежного боку
Для симетричних контурів достатньо зміщення на половину довжини:
1 @keyframes start-from-other-side { 2 from { 3 stroke-dasharray: 0 100; 4 stroke-dashoffset: 50; 5 } 6 to { 7 stroke-dasharray: 100 0; 8 stroke-dashoffset: 50; 9 } 10 } 11 svg path { 12 animation: start-from-other-side 1s ease-in-out 0s forwards; 13 }
Напрямок малювання залишається початковим, але візуально анімація починається з протилежного кінця.
Реверс напрямку та довільний старт
Щоб одночасно розвернути напрямок і змістити стартову точку, додайте повну довжину до кінцевого зміщення:
1 @keyframes other-side-and-reverse { 2 from { 3 stroke-dasharray: 0 100; 4 stroke-dashoffset: 50; 5 } 6 to { 7 stroke-dasharray: 100 0; 8 stroke-dashoffset: 150; 9 } 10 } 11 svg path { 12 animation: other-side-and-reverse 1s ease-in-out 0s forwards; 13 }
Для цілком довільної точки підставте будь-яке значення замість 50. Методом підбору (плюс-мінус 5-10 одиниць) знаходиться оптимальна позиція. В одному з проєктів стартову точку на іконці біта Twitch було зсунуто на 17 пікселів, число негарне, але візуально ідеальне.
Анімація «зсередини назовні»
Лінія росте від середини в обидва боки одночасно, класичний прийом для логотипів та інфографіки:
1 @keyframes inside-out { 2 from { 3 stroke-dasharray: 0 100; 4 stroke-dashoffset: 50; 5 } 6 to { 7 stroke-dasharray: 100 0; 8 stroke-dashoffset: 100; 9 } 10 } 11 svg path { 12 animation: inside-out 1s ease-in-out 0s forwards; 13 }
Зміщення стартує від половини довжини (центр шляху) і йде до повної довжини, витягуючи штрих за собою. Для несиметричних форм точку старту можна змістити додатково:
1 @keyframes custom-inside-out { 2 from { 3 stroke-dasharray: 0 100; 4 stroke-dashoffset: 84; 5 } 6 to { 7 stroke-dasharray: 100 0; 8 stroke-dashoffset: 134; 9 } 10 }
Тут 84 = HALF_LENGTH + 34. Константу 34 підібрано вручну для конкретної форми. Універсального рецепта немає, пробуйте, дивіться, коригуйте.
Анімація «ззовні всередину»: подвійне перо
Протилежний підхід: лінія малюється з двох кінців одночасно, сходячись до середини. На відкритих контурах це дає ефект «двох пер»:
1 @keyframes outside-in { 2 from { 3 stroke-dasharray: 0 100; 4 } 5 to { 6 stroke-dasharray: 50 0; 7 } 8 } 9 svg path { 10 animation: outside-in 1s ease-in-out 0s forwards; 11 }
Штрих доходить лише до половини довжини, розрив схлопується вдвічі швидше, ніж у базовій анімації. stroke-dashoffset тут не потрібен: візерунок «0 пробілу» в кінцевому стані сам по собі змушує обидві половинки штриха зімкнутися.
Повний контроль: початкова та кінцева точки
Коли одного напрямку та стартової точки недостатньо, можна керувати обома межами промальовування. Дві рухомі голівки лінії сходяться в спільний пункт призначення:
1 @keyframes custom-start-end { 2 from { 3 stroke-dasharray: 0 100; 4 stroke-dashoffset: 30; 5 } 6 to { 7 stroke-dasharray: 100 0; 8 stroke-dashoffset: 80; 9 } 10 } 11 svg path { 12 animation: custom-start-end 1s ease-in-out 0s forwards; 13 }
Значення 30 та 80, довільні; from-зміщення менше за to-зміщення — це єдине жорстке правило. Якщо точки близько, один сегмент летить швидко, інший повзе повільно — це нормально і навіть красиво.
Не бійтеся виходити за межі повної довжини в to-значенні. stroke-dashoffset: 130 при довжині 100 — це легально — це просто зсуває цикл штрихів на друге коло. Головне, уникати від’ємних значень.
Подвійні лінії та спецефекти
Найгнучкіший режим: штрихи половинної довжини з чверть-зсувом. Кожен шлях отримує дві лінії-голівки, що рухаються незалежно:
1 @keyframes double-lines { 2 from { 3 stroke-dasharray: 0 50; 4 stroke-dashoffset: 25; 5 } 6 to { 7 stroke-dasharray: 50 0; 8 stroke-dashoffset: 50; 9 } 10 } 11 svg path { 12 animation: double-lines 1s ease-in-out 0s forwards; 13 }
Для складних форм масив штрихів може складатися з кількох компонентів. Сума всіх значень масиву має дорівнювати повній довжині. Реальний приклад для знака нескінченності:
1 @keyframes infinity-double-lines { 2 from { 3 stroke-dasharray: 0 53 0 47; 4 stroke-dashoffset: 37; 5 } 6 to { 7 stroke-dasharray: 53 0 47 0; 8 stroke-dashoffset: 60; 9 } 10 } 11 svg .infinity { 12 animation: infinity-double-lines 8s ease-in-out 0s alternate infinite; 13 stroke: #8D69D8; 14 }
Чотири компоненти масиву (53 + 47 = 100 = повна довжина) міняються місцями між from і to. Анімацію зациклено (infinite) і реверсовано (alternate), виходить медитативний дихальний контур, який ніколи не повторюється однаково.
Відео: SVG-анімація на практиці
Щоб побачити всі описані техніки наживо та зрозуміти нюанси налаштування, перегляньте цей англомовний туторіал:
Автор покроково показує створення анімованої SVG-ілюстрації від експорту з Figma до фінальних штрихів у CSS. Таймкоди в описі дають змогу перейти одразу до потрібного розділу.
⁉️🤔 Часті запитання
Навіщо знати довжину шляху заздалегідь, якщо є getTotalLength()?
getTotalLength()— це JavaScript, а наша мета, чистий CSS без залежностей від рантайму. Ви вимірюєте довжину ОДИН раз (у консолі чи Illustrator), жорстко прописуєте число в CSS, і анімація працює без жодного рядка JS.
Чи можна анімувати кілька шляхів однією анімацією?
Так, якщо в них однакова довжина та бажана поведінка. На практиці шляхи рідко збігаються, простіше задати кожному свій
@keyframesабо використовувати CSS-змінні з різними значеннями довжини.
Чому Safari блимає при від’ємному stroke-dashoffset?
Це відомий баг рендерингу WebKit: від’ємні значення зміщення іноді інтерпретуються як миттєве скидання до 0. Лікування: завжди тримати
stroke-dashoffset≥ 0, за потреби зсуву додавайте повну довжину замість переходу в мінус.
Що робити, якщо анімація «смикається» на початку чи в кінці?
Додайте
forwardsвanimation(зберігає кінцевий стан) і приберітьalternate, якщо не потрібен реверс. Також перевірте, чи немає конфлікту з іншими CSS-правилами для того самого елемента, наприклад,transitionна тій самій властивості.
Чи працює це з stroke-dasharray на замкнених контурах зі складною геометрією?
Так, механіка єдина для будь-яких SVG-шляхів: прямих, кривих Безьє, замкнених полігонів. Різниця лише в тому, що на замкненому контурі немає візуального «початку» та «кінця», тому керування стартовою точкою особливо важливе для естетики.
Чи варто писати анімацію вручну, коли є бібліотеки?
Коротка відповідь: залежить від масштабу. Для разової іконки на лендингу ручне керування дає ідеальний результат без зайвих залежностей. П’ятнадцять хвилин на підбір зміщення, і контур малюється саме так, як ви задумали, а не як вирішив розробник бібліотеки.
Коли ми робили SVG-роздільники для Elementor, ручне керування окупилося трикратно. Плагін використовує десятки анімованих роздільників, і кожен, з індивідуально налаштованими штрихами. Жодна бібліотека не дала б такого контролю над естетикою.
Поверх технік із цієї статті накручуються:
- Керування станами анімації через JavaScript (старт/стоп/рестарт);
- Лінивий запуск через
Intersection Observer: контур малюється, тільки коли потрапляє у в’юпорт; - Ланцюжки анімацій через
animation-delay; - Кастомні функції плавності (
cubic-bezier()); - Додаткова робота з прозорістю в
@keyframes; - Еластична анімація пунктирних ліній («мурашина доріжка»).
Беріть за основу креслення зі статті, підставляйте свої значення довжин і експериментуйте з cubic-bezier(). Правильно підібрана функція плавності змінює сприйняття анімації сильніше, ніж зсув стартової точки на пару пікселів.



