Проблемы с трансформациями

Snap.svg предоставляет мощный и удобный интерфейс для работы с векторной графикой, однако трансформации объектов часто становятся источником ошибок и неожиданных результатов. Трансформации включают смещение (translate), масштабирование (scale), вращение (rotate) и наклон (skew), и их комбинации.


Порядок применения трансформаций

В Snap.svg порядок применения трансформаций критически важен. Каждая трансформация применяется относительно текущей системы координат объекта, а не глобальных координат SVG.

Пример некорректного применения:

var s = Snap("#svg");
var rect = s.rect(50, 50, 100, 50);
rect.transform('r45t100,0'); // Поворот, затем смещение

В данном случае сначала выполняется поворот относительно начала координат, затем смещение. Если необходимо поворачивать объект вокруг его центра, требуется:

rect.transform('t100,0r45,' + (rect.attr("x") + rect.attr("width")/2) + ',' + (rect.attr("y") + rect.attr("height")/2));

Ключевой момент: Snap.svg применяет трансформации в последовательности слева направо, и результат часто отличается от математически ожидаемого, если не учитывать центр вращения или локальные координаты.


Композиция трансформаций

Snap.svg хранит все трансформации объекта в виде строки transform. Например:

rect.transform('t50,50r30s2,2');
  • t50,50 — смещение по X и Y
  • r30 — вращение на 30° относительно центра (по умолчанию)
  • s2,2 — масштабирование по X и Y

При необходимости добавить новую трансформацию, важно использовать метод transform() правильно. Простое присваивание новой строки заменяет старую трансформацию:

rect.transform('s3,3'); // предыдущие t и r пропадут

Чтобы сочетать старые и новые трансформации, следует использовать:

rect.transform(rect.transform().localMatrix.toTransformString() + 's3,3');

Или применять метод matrix напрямую для точного контроля.


Проблемы с центром вращения

Многие ошибки связаны с неправильным определением центра вращения. В Snap.svg поворот без указания центра выполняется вокруг (0,0) SVG-контейнера, а не центра объекта.

Пример корректного вращения вокруг центра прямоугольника:

var cx = rect.attr("x") + rect.attr("width") / 2;
var cy = rect.attr("y") + rect.attr("height") / 2;
rect.transform('r45,' + cx + ',' + cy);

Если объект уже трансформирован, координаты центра могут быть смещены, и прямое вычисление x + width/2 не даст ожидаемого результата. В таких случаях необходимо использовать текущую матрицу трансформации:

var bbox = rect.getBBox();
rect.transform('r45,' + (bbox.cx) + ',' + (bbox.cy));

Масштабирование и влияние на позицию

Масштабирование в Snap.svg изменяет размер объекта относительно его начала координат. При комбинировании с смещением или вращением часто возникают неожиданные сдвиги.

Например:

rect.transform('s2,2t50,50'); // объект масштабируется относительно (0,0), затем смещается

Чтобы масштабировать объект относительно его центра, необходимо выполнить последовательность:

var bbox = rect.getBBox();
rect.transform('t' + (-bbox.cx) + ',' + (-bbox.cy) + 's2,2t' + bbox.cx + ',' + bbox.cy);

Это переносит объект в начало координат, масштабирует, затем возвращает на место.


Проблемы при сложных анимациях

Snap.svg поддерживает анимацию трансформаций через animate(). Ошибки возникают при попытке анимировать объект, уже имеющий трансформации:

rect.animate({ transform: 't100,0' }, 1000);

Если rect уже имеет поворот или масштаб, простое задание transform заменяет предыдущие трансформации. Решение — использовать матрицы или относительные трансформации:

var m = rect.transform().localMatrix.clone();
m.translate(100, 0);
rect.animate({ transform: m.toTransformString() }, 1000);

Проблемы с наклонами (skew)

Snap.svg применяет наклон через skewX и skewY, но они ведут себя неинтуитивно при комбинации с поворотом или масштабом. Часто возникает эффект “смещения объекта”.

Для корректного наклона рекомендуется использовать матрицы трансформации:

var matrix = new Snap.Matrix();
matrix.skewX(30);
matrix.translate(50, 50);
rect.transform(matrix);

Преимущество: точный контроль последовательности операций и центра координат, минимизация ошибок.


Рекомендации по устранению проблем

  • Всегда использовать getBBox() для вычисления центра объекта перед вращением или масштабированием.
  • При комбинированных трансформациях предпочитать матрицы, а не строковые цепочки transform.
  • Для анимаций сохранять текущую матрицу объекта и модифицировать её, вместо прямой перезаписи transform.
  • Проверять порядок применения: смещение → масштаб → поворот → наклон обычно дает более предсказуемый результат.
  • Для сложных объектов использовать вложенные группы <g> для разделения трансформаций и упрощения координатной системы.

Эти подходы минимизируют непредсказуемое поведение, которое часто встречается при работе с Snap.svg, особенно при динамическом изменении графики.