Skip to content

Все для WordPress, веб-розробки — і не тільки

✨ Анімація ліній і замкнутих контурів SVG на чистому CSS

✨ Анімація ліній і замкнутих контурів 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}
6svg 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}
11svg 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}
9svg 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}
11svg 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}
11svg 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}
11svg 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}
9svg 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}
11svg 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}
11svg 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}
11svg .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(). Правильно підібрана функція плавності змінює сприйняття анімації сильніше, ніж зсув стартової точки на пару пікселів.