Механизм перетаскивания в D3.js реализуется через модуль
d3.drag(). Он предоставляет универсальную систему обработки
перемещения элементов мышью, тач-событиями и стилусом. Во время
взаимодействия генерируются три основных события:
start — начало перетаскивания;drag — процесс перемещения;end — завершение перетаскивания.Каждое событие позволяет управлять состоянием интерфейса, координатами элементов, анимациями и логикой пользовательского взаимодействия.
Базовая структура подключения:
const dragBehavior = d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded);
d3.selectAll("circle")
.call(dragBehavior);
В этом примере:
drag;Событие start вызывается один раз в момент начала
перетаскивания.
На практике внутри start обычно выполняются:
function dragStarted(event, d) {
d3.select(this)
.attr("stroke", "black")
.attr("stroke-width", 3);
}
После начала перетаскивания элемент получает чёрную обводку.
В обработчик автоматически передаётся объект event.
Наиболее важные свойства:
| Свойство | Назначение |
|---|---|
event.x |
текущая X-координата |
event.y |
текущая Y-координата |
event.dx |
изменение по X |
event.dy |
изменение по Y |
event.subject |
объект перетаскивания |
event.active |
количество активных drag-операций |
Координаты вычисляются относительно контейнера перетаскивания.
Пример:
function dragStarted(event) {
console.log(event.x);
console.log(event.y);
}
Если элемент расположен внутри SVG, координаты будут соответствовать SVG-системе координат.
Событие drag вызывается непрерывно во время перемещения
указателя.
Именно здесь обычно изменяется положение элемента.
function dragged(event, d) {
d3.select(this)
.attr("cx", event.x)
.attr("cy", event.y);
}
Во время движения координаты круга обновляются.
<svg width="600" height="400"></svg>
const svg = d3.select("svg");
svg.append("circle")
.attr("cx", 100)
.attr("cy", 100)
.attr("r", 40)
.attr("fill", "steelblue")
.call(
d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
);
function dragStarted(event) {
d3.select(this)
.attr("fill", "orange");
}
function dragged(event) {
d3.select(this)
.attr("cx", event.x)
.attr("cy", event.y);
}
function dragEnded(event) {
d3.select(this)
.attr("fill", "steelblue");
}
Событие end вызывается после завершения
перетаскивания.
Внутри end часто выполняются:
function dragEnded(event, d) {
console.log("Перетаскивание завершено");
}
Во время перетаскивания элемент может перекрываться другими объектами.
Метод raise() перемещает его наверх.
function dragStarted(event) {
d3.select(this)
.raise()
.attr("stroke", "red");
}
D3.js тесно связывает DOM и данные.
Во время drag можно изменять сами данные объекта.
function dragged(event, d) {
d.x = event.x;
d.y = event.y;
d3.select(this)
.attr("cx", d.x)
.attr("cy", d.y);
}
Теперь координаты сохраняются не только визуально, но и внутри массива данных.
Drag можно применять к контейнеру <g>.
svg.append("g")
.call(
d3.drag()
.on("drag", function(event) {
d3.select(this)
.attr(
"transform",
`translate(${event.x}, ${event.y})`
);
})
);
Для сложных объектов удобнее применять transform.
function dragged(event) {
d3.select(this)
.attr(
"transform",
`translate(${event.x}, ${event.y})`
);
}
Такой подход особенно полезен для:
event.subject содержит объект, который участвует в
drag-операции.
function dragStarted(event) {
console.log(event.subject);
}
Можно вручную определить объект перетаскивания.
const drag = d3.drag()
.subject(function(event, d) {
return d;
});
Часто требуется запретить выход за пределы контейнера.
function dragged(event) {
let x = Math.max(0, Math.min(600, event.x));
let y = Math.max(0, Math.min(400, event.y));
d3.select(this)
.attr("cx", x)
.attr("cy", y);
}
Одно из наиболее важных применений drag — взаимодействие с
forceSimulation.
const drag = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
function dragstarted(event, d) {
if (!event.active) {
simulation.alphaTarget(0.3).restart();
}
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragended(event, d) {
if (!event.active) {
simulation.alphaTarget(0);
}
d.fx = null;
d.fy = null;
}
Свойство показывает количество активных drag-операций.
console.log(event.active);
Это особенно важно при:
d3.drag() автоматически поддерживает:
Дополнительная обработка обычно не требуется.
Метод filter() позволяет ограничить запуск drag.
const drag = d3.drag()
.filter(event => event.button === 0);
Разрешается только левая кнопка мыши.
Метод container() задаёт систему координат.
const drag = d3.drag()
.container(function() {
return this.parentNode;
});
Иногда браузер начинает выделять текст или перетаскивать изображения.
svg {
user-select: none;
}
D3.js может работать не только с SVG, но и с Canvas.
В этом случае координаты обновляются внутри структуры данных, после чего сцена перерисовывается.
function dragged(event, d) {
d.x = event.x;
d.y = event.y;
render();
}
При большом количестве элементов важно избегать тяжёлых операций
внутри drag.
function dragged(event) {
d3.selectAll("circle")
.attr("opacity", 0.5);
}
function dragged(event) {
d3.select(this)
.attr("cx", event.x);
}
drag можно совмещать с zoom.
svg.call(d3.zoom());
node.call(d3.drag());
Однако необходимо учитывать конфликт событий.
Часто drag назначается только узлам, а zoom — контейнеру.
svg.call(zoom);
nodes.call(drag);
Такое разделение предотвращает взаимное блокирование событий.
Drag-события можно отключить.
selection.on(".drag", null);
mousedown / touchstart
↓
start
↓
drag
↓
drag
↓
drag
↓
end
start.drag.end.Стрелочные функции не имеют собственного this.
.on("drag", (event) => {
d3.select(this);
})
Здесь this не будет указывать на DOM-элемент.
.on("drag", function(event) {
d3.select(this);
})
function dragged(event, d) {
console.log(d);
}
Параметр d содержит привязанные данные элемента.
Иногда требуется узнать исходное событие браузера.
console.log(event.sourceEvent);
console.log(event.sourceEvent.type);
Результат:
mousemove
touchmove
pointermove
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.call(d3.drag()
.on("drag", dragged));
Каждый объект получает собственное drag-поведение.
Drag работает не только с SVG.
d3.select(".box")
.call(
d3.drag()
.on("drag", function(event) {
d3.select(this)
.style("left", event.x + "px")
.style("top", event.y + "px");
})
);
.box {
position: absolute;
}
Во время drag удобно менять CSS-классы.
function dragStarted(event) {
d3.select(this)
.classed("active", true);
}
function dragEnded(event) {
d3.select(this)
.classed("active", false);
}
.active {
stroke: red;
stroke-width: 4px;
}
События start, drag и end
активно используются в: