В библиотеке Snap.svg перетаскивание объектов реализуется с помощью
метода drag(), который применяется к любому элементу SVG,
созданному через Snap. Основной синтаксис метода:
element.drag(onmove, onstart, onend, [mcontext], [scontext], [econtext]);
Пример простого перетаскивания прямоугольника:
var s = Snap(800, 600);
var rect = s.rect(100, 100, 150, 100).attr({fill: "#3498db"});
var startX, startY;
rect.drag(
function(dx, dy) { // onmove
this.attr({
x: startX + dx,
y: startY + dy
});
},
function() { // onstart
startX = this.attr("x");
startY = this.attr("y");
},
function() { // onend
console.log("Перетаскивание завершено");
}
);
В этом примере dx и dy — смещения
относительно начальной позиции, вычисляемые автоматически
библиотекой.
Snap.svg хранит координаты в атрибутах x и
y для прямоугольников, cx и cy
для кругов и transform для сложных фигур. Для универсальной
обработки перетаскивания удобно использовать метод
transform():
element.transform(`translate(${dx}, ${dy})`);
Однако при использовании transform() необходимо
учитывать предыдущие трансформации. Для корректного суммирования
смещений применяется метод matrix():
var matrix = element.transform().localMatrix;
matrix.e += dx;
matrix.f += dy;
element.transform(matrix);
Такой подход обеспечивает стабильное поведение элементов даже при наличии поворотов или масштабирования.
Часто требуется ограничить движение элемента рамками SVG или другими
координатами. Это делается внутри функции onmove с
проверкой смещений:
rect.drag(
function(dx, dy) {
var newX = Math.max(0, Math.min(startX + dx, 650)); // 650 = ширина SVG - ширина прямоугольника
var newY = Math.max(0, Math.min(startY + dy, 500)); // 500 = высота SVG - высота прямоугольника
this.attr({x: newX, y: newY});
},
function() {
startX = this.attr("x");
startY = this.attr("y");
}
);
Использование Math.max и Math.min
предотвращает выход элементов за границы.
Snap.svg позволяет объединять элементы в группы через
g() и задавать перетаскивание сразу всей группы:
var circle = s.circle(50, 50, 40).attr({fill: "#e74c3c"});
var text = s.text(50, 55, "Привет").attr({fill: "#fff", "text-anchor": "middle"});
var group = s.group(circle, text);
group.drag(
function(dx, dy) {
this.transform(`translate(${dx}, ${dy})`);
},
function() {},
function() {}
);
При этом dx и dy применяются ко всей
группе, что позволяет легко создавать интерактивные комбинации
объектов.
Помимо функций onstart, onmove и
onend, можно использовать стандартные события DOM для
расширенного контроля:
element.mousedown() — запуск кастомного поведения при
нажатии.element.mouseup() — завершение действия.element.mousemove() — отслеживание движения мыши без
привязки к drag.Комбинация этих событий с методами Snap.svg позволяет реализовать сложные механики, например, динамическое изменение размера или поворот объектов во время перетаскивания.
Snap.svg позволяет комбинировать перетаскивание с анимацией через
метод animate():
rect.drag(
function(dx, dy) {
this.attr({x: startX + dx, y: startY + dy});
},
function() {
startX = this.attr("x");
startY = this.attr("y");
},
function() {
this.animate({fill: "#2ecc71"}, 300);
}
);
Такой подход позволяет визуально отмечать завершение перетаскивания или изменение состояния элемента.
Иногда требуется, чтобы после отпускания элемент возвращался на
исходную позицию. Это делается в onend с помощью метода
animate:
rect.drag(
function(dx, dy) {
this.attr({x: startX + dx, y: startY + dy});
},
function() {
startX = this.attr("x");
startY = this.attr("y");
},
function() {
this.animate({x: startX, y: startY}, 500, mina.easeinout);
}
);
Метод mina.easeinout обеспечивает плавное движение
назад, создавая эффект “упругости”.
Snap.svg позволяет комбинировать перетаскивание с проверкой столкновений и другими логическими условиями. Например, для запрета наложения на другой элемент:
rect.drag(
function(dx, dy) {
var newX = startX + dx;
var newY = startY + dy;
if (!checkCollision(newX, newY, rect)) {
this.attr({x: newX, y: newY});
}
},
function() {
startX = this.attr("x");
startY = this.attr("y");
}
);
function checkCollision(x, y, elem) {
var other = s.select("#staticRect");
return x < other.attr("x") + other.attr("width") &&
x + elem.attr("width") > other.attr("x") &&
y < other.attr("y") + other.attr("height") &&
y + elem.attr("height") > other.attr("y");
}
Такой подход позволяет создавать интерактивные игры и редакторы внутри SVG, полностью управляя поведением элементов.