Snap.svg предоставляет удобные методы для реализации перетаскивания
элементов SVG с помощью мыши или тач-событий. Ключевой механизм основан
на методе .drag(), который позволяет назначить функции для
трёх событий: onMove, onStart,
onEnd.
element.drag(onMove, onStart, onEnd);
onMove(dx, dy, x, y, event) – вызывается при перемещении элемента.
dx и dy – смещение по осям X и Y
относительно позиции начала перетаскивания.x и y – текущие координаты курсора
относительно SVG.event – объект события браузера.onStart(x, y, event) – вызывается при начале перетаскивания. Позволяет сохранять исходные координаты элемента или устанавливать состояние.
onEnd(event) – вызывается при завершении перетаскивания, можно реализовать логику «отпускания» или проверки позиции.
var s = Snap("#svg");
var circle = s.circle(100, 100, 50).attr({ fill: "#3498db", stroke: "#2980b9", strokeWidth: 3 });
circle.drag(
function(dx, dy) {
this.attr({ cx: this.data('ox') + dx, cy: this.data('oy') + dy });
},
function() {
this.data('ox', this.attr('cx'));
this.data('oy', this.attr('cy'));
},
function() {
console.log('Перетаскивание завершено');
}
);
Здесь data('ox') и data('oy') используются
для хранения исходных координат элемента, что позволяет правильно
вычислять смещение при движении.
Для создания ограничений движения используется проверка координат
внутри onMove. Например, чтобы элемент оставался внутри
SVG-контейнера:
var width = s.node.clientWidth;
var height = s.node.clientHeight;
circle.drag(
function(dx, dy) {
var newX = this.data('ox') + dx;
var newY = this.data('oy') + dy;
newX = Math.max(50, Math.min(width - 50, newX));
newY = Math.max(50, Math.min(height - 50, newY));
this.attr({ cx: newX, cy: newY });
},
function() {
this.data('ox', this.attr('cx'));
this.data('oy', this.attr('cy'));
},
function() {}
);
Здесь используется метод Math.max и
Math.min для ограничения координат, учитывая радиус
круга.
Для групп элементов (<g>), состоящих из нескольких
фигур, также можно использовать .drag(). В этом случае
полезно применять трансформации через transform():
var group = s.group(
s.rect(0, 0, 100, 50).attr({ fill: "#e74c3c" }),
s.text(10, 30, "Перетащи меня").attr({ fill: "#fff", fontSize: 16 })
);
group.drag(
function(dx, dy) {
this.transform(this.data('origTransform') + "t" + dx + "," + dy);
},
function() {
this.data('origTransform', this.transform().local);
},
function() {}
);
Использование трансформации t(dx, dy) позволяет
перемещать всю группу без изменения координат отдельных элементов.
Snap.svg поддерживает плавное изменение атрибутов во время drag:
circle.drag(
function(dx, dy) { this.attr({ cx: this.data('ox') + dx, cy: this.data('oy') + dy }); },
function() { this.data('ox', this.attr('cx')); this.data('oy', this.attr('cy')); this.attr({ fill: "#2ecc71" }); },
function() { this.attr({ fill: "#3498db" }); }
);
var points = [{x:200,y:200},{x:400,y:100}];
circle.drag(
function(dx, dy) {
var newX = this.data('ox') + dx;
var newY = this.data('oy') + dy;
this.attr({ cx: newX, cy: newY });
},
function() {
this.data('ox', this.attr('cx'));
this.data('oy', this.attr('cy'));
},
function() {
var closest = points.reduce((a, b) => {
var da = Math.hypot(a.x - this.attr('cx'), a.y - this.attr('cy'));
var db = Math.hypot(b.x - this.attr('cx'), b.y - this.attr('cy'));
return da < db ? a : b;
});
this.attr({ cx: closest.x, cy: closest.y });
}
);
Эта техника полезна для создания snap-to-grid интерфейсов и игр с перетаскиванием объектов.
Snap.svg корректно работает с тач-событиями на мобильных устройствах.
Метод .drag() автоматически поддерживает
touchstart, touchmove и touchend.
Для мультитач можно расширить логику с использованием
event.touches и вычислять среднюю позицию пальцев для
перемещения группы элементов.
group и
трансформации вместо изменения атрибутов координат каждого элемента
отдельно.onMove;
лучше хранить промежуточные данные в data() объекта.requestAnimationFrame при необходимости интеграции drag с
анимациями.Snap.svg позволяет реализовать возврат элемента на исходную позицию
через анимацию в onEnd:
circle.drag(
function(dx, dy) { this.attr({ cx: this.data('ox') + dx, cy: this.data('oy') + dy }); },
function() { this.data('ox', this.attr('cx')); this.data('oy', this.attr('cy')); },
function() {
this.animate({ cx: this.data('ox'), cy: this.data('oy') }, 300, mina.easeinout);
}
);
Использование mina.easeinout создаёт плавный эффект
возврата, повышая интерактивность интерфейса.
data().Math.min/Math.max или логические
проверки.onStart и
onEnd для улучшения UX.requestAnimationFrame и минимизировать вычисления в
onMove.Snap.svg делает drag and drop интуитивным и гибким, позволяя создавать интерактивные диаграммы, игры и визуальные редакторы на основе SVG.