Snap.svg предоставляет мощные инструменты для реализации
интерактивного перетаскивания (drag) элементов SVG. Метод
drag позволяет привязать к элементу три функции:
Простейший синтаксис:
element.drag(onMove, onStart, onEnd);
element – объект Snap.svg (paper.circle(),
paper.rect() и т.д.).onMove получает параметры
(dx, dy, x, y, event), где dx и
dy – смещение от точки начала drag, x и
y – текущие координаты курсора, event – объект
события.onStart вызывается без аргументов, но часто
используется для запоминания исходного состояния элемента.onEnd вызывается после завершения перетаскивания для
выполнения финальных действий.var s = Snap(800, 600);
var circle = s.circle(100, 100, 50).attr({fill: "#4CAF50"});
var startX, startY;
circle.drag(
function(dx, dy) { // onMove
this.attr({
cx: startX + dx,
cy: startY + dy
});
},
function() { // onStart
startX = this.attr("cx");
startY = this.attr("cy");
},
function() { // onEnd
console.log("Перетаскивание завершено");
}
);
В этом примере круг можно перемещать мышью. Значения dx
и dy обеспечивают точное смещение относительно начальной
позиции.
Для создания ограничений перемещения используют проверку координат в
функции onMove. Например, чтобы круг нельзя было вывести за
пределы канвы:
circle.drag(
function(dx, dy) {
let newX = Math.min(Math.max(startX + dx, 50), 750);
let newY = Math.min(Math.max(startY + dy, 50), 550);
this.attr({cx: newX, cy: newY});
},
function() {
startX = this.attr("cx");
startY = this.attr("cy");
}
);
Здесь Math.min и Math.max ограничивают
координаты, учитывая радиус круга.
Если требуется, чтобы элемент не «скакал» к позиции курсора, важно хранить разницу между координатами курсора и координатами элемента:
var offsetX, offsetY;
circle.drag(
function(dx, dy, x, y) {
this.attr({
cx: x - offsetX,
cy: y - offsetY
});
},
function(startX, startY, event) {
var bbox = this.getBBox();
offsetX = startX - bbox.cx;
offsetY = startY - bbox.cy;
}
);
Такой подход особенно полезен для сложных SVG-объектов, где точка привязки не совпадает с центром фигуры.
Для организации drag группы элементов можно использовать
Snap.g():
var group = s.group(
s.rect(0, 0, 100, 50).attr({fill: "#2196F3"}),
s.text(50, 30, "Перетащи меня").attr({fill: "#fff", "text-anchor":"middle"})
);
var startX, startY;
group.drag(
function(dx, dy) {
this.attr({
transform: `translate(${startX + dx}, ${startY + dy})`
});
},
function() {
var t = this.transform().localMatrix;
startX = t.e;
startY = t.f;
}
);
Применение transform позволяет перемещать всю группу как
единое целое, сохраняя относительное положение элементов внутри.
Иногда нужно движение только по одной оси. Для ограничения по X или Y:
// Только по X
circle.drag(
function(dx) {
this.attr({cx: startX + dx});
},
function() {
startX = this.attr("cx");
}
);
// Только по Y
circle.drag(
function(_, dy) {
this.attr({cy: startY + dy});
},
function() {
startY = this.attr("cy");
}
);
Использование _ в параметрах позволяет игнорировать
ненужное смещение.
Для плавного «отпуска» объекта после drag можно реализовать простую
инерцию через setInterval или
requestAnimationFrame:
var velocity = {x:0, y:0};
circle.drag(
function(dx, dy) {
this.attr({cx: startX + dx, cy: startY + dy});
velocity.x = dx - (velocity.prevX || 0);
velocity.y = dy - (velocity.prevY || 0);
velocity.prevX = dx;
velocity.prevY = dy;
},
function() {
startX = this.attr("cx");
startY = this.attr("cy");
velocity = {x:0, y:0};
},
function() {
var elem = this;
function animate() {
velocity.x *= 0.9;
velocity.y *= 0.9;
startX += velocity.x;
startY += velocity.y;
elem.attr({cx: startX, cy: startY});
if(Math.abs(velocity.x) > 0.5 || Math.abs(velocity.y) > 0.5) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
}
);
Такой подход создаёт ощущение естественной физики, когда объект продолжает движение после отпускания.
Snap.svg позволяет комбинировать drag с другими
событиями (mouseover, mouseout,
click), обеспечивая гибкое управление элементами:
circle.hover(
function() { this.attr({fill: "#FF5722"}); },
function() { this.attr({fill: "#4CAF50"}); }
);
circle.click(function() {
console.log("Клик на элементе!");
});
Комбинируя drag с hover и click, можно создавать сложные интерактивные интерфейсы SVG.
Для сохранения положения элементов после drag удобно использовать
атрибут data или собственные свойства объекта:
circle.drag(
function(dx, dy) {
this.attr({cx: startX + dx, cy: startY + dy});
},
function() { startX = this.attr("cx"); startY = this.attr("cy"); },
function() {
this.data("finalPosition", {x: this.attr("cx"), y: this.attr("cy")});
}
);
Позже можно получить сохранённую позицию через
circle.data("finalPosition").
Эти техники позволяют строить сложную пользовательскую логику drag, создавать интерактивные SVG-приложения и игры, а также контролировать движения объектов с высокой точностью и гибкостью.