У вас же было такое, что ждёте курьера или доставку, а их всё нет и нет? Заходите сайт, обновляете статус, а там «В пути» и больше никакой информации. У нас вот было много раз.

В хороших случаях курьера рисуют прямо на карте, и показывают, где он едет, где проехал, и прямо анимацией можно всё рассмотреть.

Один из способов сделать такое чудо (а еще анимацию по любой траектории) — свойство offset-path, для анимации движения элементов вообще без использования JavaScript.

Например, вы хотите, чтобы элемент перемещался по кругу, зигзагом или даже по спирали. Раньше для таких задач приходилось использовать JavaScript или сложные расчёты. Допустим, двигаем квадратик:

<div class="box"></div>

Вот его стили:

.box {
  position: absolute; /* Необходимо для движения по координатам */
  width: 50px;
  height: 50px;
  background-color: tomato;
}

И вот таким длинным JavaScript-кодом пришлось бы двигать квадратик по сложной траектории:

// Функция для движения по SVG-пути
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d', 'M 0 0 C 100 100 200 0 300 100');

// Получаем элемент
const box = document.querySelector('.box');

// Длительность анимации в миллисекундах
const duration = 3000;
let startTime;

// Функция анимации
function animate(timestamp) {
  if (!startTime) startTime = timestamp;

  // Вычисляем прошедшее время
  const progress = (timestamp - startTime) / duration;

  // Останавливаем анимацию после окончания
  if (progress > 1) {
	startTime = timestamp;
  }

  // Получаем координаты точки на пути (значение от 0 до 1)
  const point = path.getPointAtLength(path.getTotalLength() * progress);

  // Перемещаем элемент в полученные координаты
  box.style.transform = `translate(${point.x}px, ${point.y}px)`;

  // Зацикливаем анимацию
  requestAnimationFrame(animate);
}

// Запускаем анимацию
requestAnimationFrame(animate);

Теперь можно обойтись только CSS с помощью Offset-path.

Как работает offset-path?

Свойство offset-path принимает несколько значений:

  • path() — позволяет создать собственный путь движения.
  • circle() — задаёт движение по окружности;
  • ray() — создаёт движение по прямой линии, заданной углом и длиной;
  • none — выключает перемещение по траектории.

Пример использования

Создадим элемент, который движется по кривой траектории:

<div class="box"></div>
.box {
  width: 50px;
  height: 50px;
  background-color: tomato;
  offset-path: path('M 0 0 C 100 100 200 0 300 100');
  animation: move 3s linear infinite;
}

@keyframes move {
  to {
    offset-distance: 100%;
  }
}

В этом примере мы создали путь с помощью SVG, запустили анимацию, используя свойство offset-distance, чтобы указать, какое расстояние проходит элемент по заданному пути.

Что ещё полезно знать? Есть несколько дополнительных полезных свойств, которые идут вместе с offset-path:

  • offset-distance управляет позицией элемента на заданном пути в процентах или других единицах длины.
.element {
  offset-distance: 50%; /* Позиция на середине пути */
  offset-distance: 100px; /* Смещение на 100 пикселей по пути от начала */
}
  • offset-rotate задаёт поворот элемента во время движения (например, auto, чтобы элемент автоматически поворачивался вдоль пути).

Разные варианты траекторий

Оставляем вам разные варианты траекторий, которые могут пригодиться в реальных проектах.

Глаз

.element {
  offset-path: path('M50,150 Q150,10 250,150 Q150,290 50,150 Z ');
}

Волна

.element {
  offset-path: path('M0,50 Q50,0 100,50 T200,50 T300,50');
}

Знак бесконечности

.element {
  offset-path: path('M150,150 C120,100 60,100 50,150 C40,200 110,200 150,150 C190,100 260,100 250,150 C240,200 180,200 150,150');
}

Треугольник

.element {
  offset-path: path('M150,20 L280,260 L20,260 Z');
}


Звезда

.element {
  offset-path: path('M150,20 L179,111 L270,111 L197,165 L223,255 L150,200 L77,255 L103,165 L30,111 L121,111 Z');
}

Сердечко

.element {
  offset-path: path('M150,100 A40,40 0 0,1 230,100 Q230,160 150,240 Q70,160 70,100 A40,40 0 0,1 150,100 Z');
}

Прямоугольник со скругленными углами

.element {
  offset-path: path('M50,50 H250 A20,20 0 0,1 270,70 V230 A20,20 0 0,1 250,250 H50 A20,20 0 0,1 30,230 V70 A20,20 0 0,1 50,50 Z');
}

Капля

.element {
  offset-path: path('M150,40 Q220,40 220,120 Q220,180 150,260 Q80,180 80,120 Q80,40 150,40 Z');
}

Облачко

.element {
  offset-path: path('M60,170 Q40,100 90,100 Q110,60 150,80 Q190,50 220,90 Q260,90 250,140 Q280,160 250,190 Q220,240 160,220 Q110,250 90,200 Q40,210 60,170 Z');
}

Ромб

.element {
  offset-path: path('M150,20 L280,150 L150,280 L20,150 Z');
}

Поддержка в браузерах

Свойство offset-path хорошо поддерживается во всех современных браузерах. Тем не менее, рекомендуется проверять поддержку для каждого конкретного случая.