SVG Path Data API: полное руководство для веб-разработчиков 2026
Руководство · Июнь 2026

SVG Path Data API:
Полное руководство для веб-разработчиков

Firefox 137 представил SVG Path Data API — нативный способ читать, создавать и управлять SVG-путями программно в JavaScript. Изучите getPathData(), setPathData() и getPathSegmentAtLength() с реальными примерами кода.

Олег Максимов 4 июня 2026 18 мин чтения

Введение

Более двух десятилетий работа с SVG-путями в JavaScript означала одно: парсинг и сборку строк. Каждый <path d="M10 10 L 20 20..."> требовал разбиения атрибута d, токенизации команд, обработки неявных команд и обратной сборки — всё через конкатенацию строк. Это было хрупко, чревато ошибками и абсолютно непрозрачно для проверки типов.

SVG Path Data API (доступен в Firefox 137+, редакторский черновик W3C от сентября 2025) полностью меняет это. API добавляет три метода на SVGPathElementgetPathData(), setPathData() и getPathSegmentAtLength() — которые представляют данные SVG-пути как структурированные JavaScript-объекты вместо непрозрачных строк.

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

Проблема: строки SVG-путей хрупки

До Path Data API любое манипулирование SVG-путём включало работу с атрибутом d — строкой вроде "M 10 10 L 20 20 L 30 10 Z". Чтобы программно изменить путь, нужно было:

  1. Прочитать element.getAttribute('d')
  2. Токенизировать — разделить по пробелам и запятым, сохраняя отрицательные числа
  3. Определить команды (M, L, C, Q, A, Z и т.д.) и их координаты
  4. Обнаружить неявные команды (последовательные L после M без буквы)
  5. Изменить нужный сегмент
  6. Собрать всё обратно в строку
  7. Вызвать element.setAttribute('d', новаяСтрока)

Любая ошибка в парсере — пропущенная неявная команда, неверное количество контрольных точек Безье или отсутствующий пробел перед отрицательным числом — могла незаметно сломать весь SVG. Отладка превращалась в разглядывание длинных строк чисел. Этот класс проблем должен был получить нативное API годы назад.

💡 Вывод: Строковое манипулирование SVG-путями чревато ошибками, сложно в отладке и совершенно непригодно для сложных операций, таких как морфинг анимации или интерактивные редакторы. Path Data API решает это структурированными типизированными данными.

Обзор API

SVG Path Data API добавляет три метода к SVGPathElement, каждый из которых решает конкретную задачу в рабочем процессе управления путями:

Метод Назначение Возвращает
getPathData() Чтение всех сегментов пути как структурированных объектов PathDataSegment[]
setPathData(segments) Замена пути новым массивом сегментов undefined
getPathSegmentAtLength(distance) Определение сегмента на заданном расстоянии от начала пути PathDataSegment | null

Тип PathDataSegment

Каждый сегмент, возвращаемый API, следует единой структуре:

interface PathDataSegment {
  type: string;     // Команда: 'M', 'L', 'C', 'Q', 'A', 'Z' и т.д.
  values: number[]; // Координаты для этой команды
}

Свойство type использует те же одно- или двухсимвольные коды команд, что и синтаксис строки SVG-пути: M (moveto), L (lineto), C (кубическая кривая Безье), Q (квадратичная кривая), A (дуга), Z (закрытие пути). Строчные буквы означают относительные координаты; заглавные — абсолютные.

Массив values содержит ровно столько координат, сколько нужно для данного типа команды:

Команда Тип Values Описание
M, m Moveto [x, y] Перемещение в точку
L, l Lineto [x, y] Прямая линия
H, h Горизонтальная линия [x] Горизонтальная линия
V, v Вертикальная линия [y] Вертикальная линия
C, c Кубическая Безье [x1, y1, x2, y2, x, y] Две контрольные точки + конечная
S, s Гладкая кубическая Безье [x2, y2, x, y] Отражённая контрольная + конечная
Q, q Квадратичная Безье [x1, y1, x, y] Одна контрольная + конечная
T, t Гладкая квадратичная Безье [x, y] Отражённая контрольная + конечная
A, a Дуга [rx, ry, xAxisRot, largeArcFlag, sweepFlag, x, y] Эллиптическая дуга
Z, z Закрытие пути [] Закрытие текущего подпути

Чтение данных пути с getPathData()

Метод getPathData() — самый простой из трёх. Вызовите его на любом элементе <path> и получите чистый массив объектов сегментов:

const path = document.querySelector('svg path');

// Старый способ — хрупкий парсинг строки
const oldD = path.getAttribute('d');
// "M10 80 Q 52.5 10, 95 80 T 180 80 Z"

// Новый способ — структурированные данные
const segments = path.getPathData();
console.log(segments);
// [
//   { type: 'M', values: [10, 80] },
//   { type: 'Q', values: [52.5, 10, 95, 80] },
//   { type: 'T', values: [180, 80] },
//   { type: 'Z', values: [] }
// ]

Каждый сегмент — это чистый объект с явным типом и значениями. Больше не нужно гадать, означает ли "10-20" [10, -20] или [10, 20]. Больше не нужно писать собственный токенизатор SVG-пути.

Подсчёт и анализ сегментов

Со структурированными данными анализ пути тривиален:

function analyzePath(pathElement) {
  const segments = pathElement.getPathData();

  return {
    totalSegments: segments.length,
    commandTypes: segments.reduce((acc, s) => {
      acc[s.type] = (acc[s.type] || 0) + 1;
      return acc;
    }, {}),
    boundingBox: calculateBoundingBox(segments),
    isClosed: segments[segments.length - 1]?.type === 'Z'
  };
}

function calculateBoundingBox(segments) {
  let minX = Infinity, minY = Infinity;
  let maxX = -Infinity, maxY = -Infinity;

  for (const seg of segments) {
    const { type, values } = seg;
    if (type === 'Z' || type === 'z') continue;

    const endIdx = getEndCoordinateIndex(type);
    if (endIdx >= 0) {
      const x = values[endIdx - 1];
      const y = values[endIdx];
      minX = Math.min(minX, x);
      minY = Math.min(minY, y);
      maxX = Math.max(maxX, x);
      maxY = Math.max(maxY, y);
    }
  }

  return { minX, minY, maxX, maxY, width: maxX - minX, height: maxY - minY };
}

function getEndCoordinateIndex(type) {
  const ends = { M: 2, L: 2, H: 1, V: 1, C: 6, S: 4, Q: 4, T: 2, A: 6 };
  return ends[type.toUpperCase()] - 1;
}

Раньше для извлечения информации о границах из SVG-путей требовался парсинг строки d с помощью регулярных выражений — и каждое регулярное выражение было хрупким приближением. Теперь это просто обход массива.

Изменение путей с setPathData()

Настоящая сила API проявляется в setPathData(). Вместо сборки строки вы передаёте массив объектов PathDataSegment:

const path = document.querySelector('svg path');

// Читаем существующий путь
const segments = path.getPathData();

// Изменяем второй сегмент — перемещаем его конечную точку
segments[1] = {
  type: 'L',
  values: [150, 200]
};

// Записываем обратно — конкатенация строк не нужна
path.setPathData(segments);

Это значительно безопаснее строковых манипуляций. Каждый сегмент — изолированный объект. Изменение одного сегмента не может случайно повредить остальной путь, в отличие от конкатенации строк, где пропущенный пробел или лишний символ ломает всё.

Создание путей с нуля

Вы также можете строить пути целиком на JavaScript, не прикасаясь к строкам:

function createStarPath(cx, cy, outerR, innerR, points) {
  const segments = [];
  const step = Math.PI / points;

  for (let i = 0; i < points * 2; i++) {
    const r = i % 2 === 0 ? outerR : innerR;
    const angle = i * step - Math.PI / 2;
    const x = cx + r * Math.cos(angle);
    const y = cy + r * Math.sin(angle);

    if (i === 0) {
      segments.push({ type: 'M', values: [x, y] });
    } else {
      segments.push({ type: 'L', values: [x, y] });
    }
  }

  segments.push({ type: 'Z', values: [] });
  return segments;
}

// Создаём пятиконечную звезду
const starSegments = createStarPath(100, 100, 80, 40, 5);
pathElement.setPathData(starSegments);

Эта функция генерирует идеально масштабированную звезду, используя только структурированные данные. Никакого построения строк, экранирования или граничных случаев с отрицательными числами — только чистая арифметика и конструирование объектов.

Трансформация координат пути

Частая задача — масштабирование или смещение координат пути. Со старым API нужно было парсить строку, извлекать координаты, изменять их и собирать строку заново. С Path Data API:

function scalePath(pathElement, scaleX, scaleY) {
  const segments = pathElement.getPathData();

  for (const seg of segments) {
    if (seg.type === 'Z' || seg.type === 'z') continue;

    for (let i = 0; i < seg.values.length; i++) {
      if (i % 2 === 0) {
        seg.values[i] *= scaleX;  // X координата
      } else {
        seg.values[i] *= scaleY;  // Y координата
      }
    }
  }

  pathElement.setPathData(segments);
}

// Масштабируем путь в 1.5 раза по горизонтали
scalePath(myPath, 1.5, 1);

Морфинг путей и анимация форм

Один из самых интересных сценариев — морфинг пути: плавная анимация перехода от одной формы к другой. Path Data API делает это значительно проще, потому что исходная и целевая формы — массивы структурированных объектов одного формата:

function morphPath(pathElement, targetSegments, duration = 1000) {
  const startSegments = pathElement.getPathData();

  if (startSegments.length !== targetSegments.length) {
    console.warn('Количество сегментов не совпадает — морфинг может выглядеть некорректно');
  }

  const startTime = performance.now();

  function animate(currentTime) {
    const elapsed = currentTime - startTime;
    const progress = Math.min(elapsed / duration, 1);
    const eased = easeInOutCubic(progress);

    const currentSegments = startSegments.map((start, i) => {
      if (i >= targetSegments.length) return start;

      const target = targetSegments[i];
      const values = start.values.map((v, j) => {
        const targetVal = target.values[j] ?? v;
        return v + (targetVal - v) * eased;
      });

      return { type: target.type, values };
    });

    pathElement.setPathData(currentSegments);

    if (progress < 1) {
      requestAnimationFrame(animate);
    }
  }

  requestAnimationFrame(animate);
}

function easeInOutCubic(t) {
  return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
}

// Пример: морфинг квадрата в круг
const square = [
  { type: 'M', values: [50, 50] },
  { type: 'L', values: [150, 50] },
  { type: 'L', values: [150, 150] },
  { type: 'L', values: [50, 150] },
  { type: 'Z', values: [] }
];

const circle = [
  { type: 'M', values: [100, 50] },
  { type: 'C', values: [127.6, 50, 150, 72.4, 150, 100] },
  { type: 'C', values: [150, 127.6, 127.6, 150, 100, 150] },
  { type: 'C', values: [72.4, 150, 50, 127.6, 50, 100] },
  { type: 'C', values: [50, 72.4, 72.4, 50, 100, 50] },
  { type: 'Z', values: [] }
];

morphPath(myPath, circle, 2000);

💡 Ключевая идея: Для плавного морфинга оба пути должны иметь одинаковое количество сегментов и одинаковые типы команд на соответствующих позициях. Если это невозможно, добавьте промежуточные шаги или нормализуйте сегменты перед морфингом.

Определение точки с getPathSegmentAtLength()

Третий метод, getPathSegmentAtLength(distance), отвечает на вопрос, который раньше требовал сложных геометрических библиотек: «Какой сегмент пути находится на данном расстоянии от начала?»

const path = document.querySelector('svg path');
const totalLength = path.getTotalLength();

// Находим сегмент на 50% длины пути
const midpoint = path.getPathSegmentAtLength(totalLength / 2);
console.log(midpoint);
// { type: 'C', values: [x1, y1, x2, y2, x, y] }

Это незаменимо для:

Поэтапное рисование пути

Практический пример — отрисовка пути сегмент за сегментом:

async function progressiveDraw(pathElement, durationPerSegment = 300) {
  const segments = pathElement.getPathData();

  const partialSegments = [segments[0]];
  pathElement.setPathData(partialSegments);

  for (let i = 1; i < segments.length; i++) {
    if (segments[i].type === 'Z' || segments[i].type === 'z') {
      partialSegments.push(segments[i]);
      pathElement.setPathData(partialSegments);
      break;
    }

    partialSegments.push(segments[i]);
    pathElement.setPathData(partialSegments);

    await new Promise(r => setTimeout(r, durationPerSegment));
  }
}

Визуализация данных с генерированными путями

Path Data API — естественный выбор для программного построения графиков и визуализации данных. Вот простой генератор линейного графика:

function createLineChart(dataPoints, width, height, padding = 20) {
  if (dataPoints.length < 2) return [];

  const xScale = (width - 2 * padding) / (dataPoints.length - 1);
  const yMin = Math.min(...dataPoints);
  const yMax = Math.max(...dataPoints);
  const yRange = yMax - yMin || 1;

  const segments = [];

  for (let i = 0; i < dataPoints.length; i++) {
    const x = padding + i * xScale;
    const y = height - padding - ((dataPoints[i] - yMin) / yRange) * (height - 2 * padding);

    if (i === 0) {
      segments.push({ type: 'M', values: [x, y] });
    } else {
      const prevX = padding + (i - 1) * xScale;
      const prevY = height - padding - ((dataPoints[i - 1] - yMin) / yRange) * (height - 2 * padding);
      const cpx1 = prevX + xScale * 0.5;
      const cpx2 = x - xScale * 0.5;

      segments.push({
        type: 'C',
        values: [cpx1, prevY, cpx2, y, x, y]
      });
    }
  }

  return segments;
}

// Использование
const data = [10, 45, 30, 70, 55, 90, 85];
const chartSegments = createLineChart(data, 400, 200);
chartPath.setPathData(chartSegments);

Интерактивный редактор путей

Самый амбициозный сценарий — интерактивный редактор SVG-путей. С помощью getPathData() и setPathData() можно собрать редактор с перетаскиванием точек в удивительно малом количестве строк:

class PathEditor {
  constructor(pathElement) {
    this.path = pathElement;
    this.segments = pathElement.getPathData();
    this.dragging = null;
    this.bindEvents();
  }

  getPointPositions() {
    const points = [];
    for (const seg of this.segments) {
      if (seg.type === 'Z' || seg.type === 'z') continue;
      const endIdx = [6, 4, 2].find(i => seg.values.length >= i) ?? 2;
      if (endIdx >= 2) {
        points.push({
          segIndex: this.segments.indexOf(seg),
          valIndexX: endIdx - 2,
          valIndexY: endIdx - 1,
          x: seg.values[endIdx - 2],
          y: seg.values[endIdx - 1]
        });
      }
    }
    return points;
  }

  bindEvents() {
    const svg = this.path.closest('svg');

    svg.addEventListener('mousedown', (e) => {
      const rect = svg.getBoundingClientRect();
      const mx = e.clientX - rect.left;
      const my = e.clientY - rect.top;

      const points = this.getPointPositions();
      let minDist = Infinity;
      for (const p of points) {
        const dist = Math.hypot(p.x - mx, p.y - my);
        if (dist < minDist && dist < 10) {
          minDist = dist;
          this.dragging = p;
        }
      }
    });

    svg.addEventListener('mousemove', (e) => {
      if (!this.dragging) return;

      const rect = svg.getBoundingClientRect();
      const seg = this.segments[this.dragging.segIndex];

      if (seg) {
        seg.values[this.dragging.valIndexX] = e.clientX - rect.left;
        seg.values[this.dragging.valIndexY] = e.clientY - rect.top;
        this.path.setPathData(this.segments);
      }
    });

    svg.addEventListener('mouseup', () => {
      this.dragging = null;
    });
  }
}

// Использование
const editor = new PathEditor(document.querySelector('svg path'));

Это полнофункциональный редактор путей менее чем в 60 строках. Ключевая идея в том, что setPathData() принимает тот же формат, который возвращает getPathData() — поэтому обработчик перетаскивания просто изменяет массив values и записывает его обратно.

Поддержка браузеров и определение поддержки

По состоянию на июнь 2026 года SVG Path Data API имеет ограниченную поддержку браузеров:

Определение поддержки (Feature Detection)

Всегда проверяйте поддержку перед использованием API:

function supportsPathDataAPI() {
  const path = document.createElementNS(
    'http://www.w3.org/2000/svg', 'path'
  );
  return 'getPathData' in path;
}

if (supportsPathDataAPI()) {
  const segments = myPath.getPathData();
} else {
  const dString = myPath.getAttribute('d');
  const segments = parsePathString(dString);
}

Стратегия полифилла

Для production используйте полифилл, оборачивающий старый строковый API:

function polyfillPathData() {
  if (supportsPathDataAPI()) return;

  SVGPathElement.prototype.getPathData = function() {
    return parseDString(this.getAttribute('d') || '');
  };

  SVGPathElement.prototype.setPathData = function(segments) {
    this.setAttribute('d', segmentsToDString(segments));
  };
}

function segmentsToDString(segments) {
  return segments.map(s => {
    if (s.type === 'Z' || s.type === 'z') return 'Z';
    return s.type + ' ' + s.values.join(' ');
  }).join(' ');
}

polyfillPathData();

⚠️ Для Production: Полифилл выше упрощён для иллюстрации. Production-полифилл должен обрабатывать неявные повторяющиеся команды (например, "M 10 10 L 20 20 30 30"), флаги дуг и научную нотацию. Рассмотрите использование community-полифилла для продакшна.

Ограничения

Заключение

SVG Path Data API — давно назревшее дополнение веб-платформы, которое наконец приносит структурированную работу с данными в SVG-пути. Он заменяет два десятилетия хрупких обходных путей парсинга строк чистыми типизированными JavaScript-объектами.

Ключевые выводы:

Начинайте экспериментировать с SVG Path Data API сегодня. Даже с текущими ограничениями браузеров API достаточно стабилен для экспериментальных проектов, а полифиллы хорошо работают в production, пока другие браузеры догоняют.

Хотите построить интерактивные SVG-визуализации? Я занимаюсь фронтенд-архитектурой, разработкой на SVG/Canvas и визуализацией данных. Посмотрите мои услуги или свяжитесь со мной для консультации.

FAQ

Что такое SVG Path Data API?
SVG Path Data API предоставляет три метода на SVGPathElement — getPathData(), setPathData() и getPathSegmentAtLength() — для чтения, изменения и создания SVG-путей программно в JavaScript. API заменяет хрупкий парсинг строк атрибута d на структурированный массив объектов сегментов пути.
В каких браузерах работает SVG Path Data API?
По состоянию на июнь 2026 года SVG Path Data API поддерживается только в Firefox 137+. Chrome, Edge и Safari пока не реализовали его. Спецификация W3C находится на стадии Editor's Draft (сентябрь 2025). Используйте определение поддержки (feature detection) и полифиллы для неподдерживаемых браузеров.
Что возвращает getPathData()?
getPathData() возвращает массив объектов PathDataSegment. Каждый сегмент содержит строку type (например, 'M' — moveto, 'L' — lineto, 'C' — кубическая кривая Безье) и массив values с числовыми координатами команды. Например, сегмент 'M' имеет values [x, y], а 'C' — values [x1, y1, x2, y2, x, y].
Чем setPathData() лучше прямой установки d-строки?
setPathData() принимает структурированный массив объектов сегментов вместо строки. Это устраняет ошибки конкатенации строк, улучшает производительность за счёт отсутствия внутреннего парсинга SVG-пути и обеспечивает типобезопасное программное создание путей. Можно передавать тот же формат, который возвращает getPathData(), делая код трансформации путей чище и предсказуемее.
Что можно построить с помощью SVG Path Data API?
API открывает несколько мощных сценариев: (1) морфинг SVG-иконок и анимация форм на JavaScript, (2) программная генерация путей для визуализации данных и графиков, (3) интерактивные редакторы SVG-путей с манипуляциями в реальном времени, (4) инструменты анализа SVG-путей с getPathSegmentAtLength(), (5) параметризованная SVG-графика, реагирующая на данные или пользовательский ввод.
Как определить поддержку SVG Path Data API?
Используйте оператор in на экземпляре SVGPathElement: 'getPathData' in document.createElementNS('http://www.w3.org/2000/svg', 'path'). Если true — API доступен. Всегда используйте определение поддержки и предусматривайте graceful fallback — парсите d-атрибут вручную или используйте полифилл.
Как работает getPathSegmentAtLength()?
Метод getPathSegmentAtLength(distance) принимает расстояние от начала пути и возвращает объект PathDataSegment, находящийся на этой дистанции. Это полезно для интерактивной подсветки сегментов, анимации поэтапного рисования, аннотаций на определённых участках пути и анализа границ сегментов.
Контакты

Создаёте интерактивные SVG-визуализации?

Я создаю веб-приложения с использованием SVG, Canvas и современного JavaScript. Расскажите о задаче — бесплатная консультация.