circles = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
svg.append("defs")
.append("filter")
.attr("id", "shadow")
.append("feDropShadow")
.attr("dx", 2)
.attr("dy", 2)
.attr("stdDeviation", 4)
.attr("flood-color", "black");
const drag = d3.drag()
.on("start", startDragging)
.on("drag", dragCircle)
.on("end", endDragging);
const rect = svg.append("rect")
.attr("x", width / 5 * 3)
.attr("y", (height - rectSize) / 2)
.attr("width", rectSize)
.attr("height", rectSize)
.attr("rx", 6);
updateRect(false);
function updateRect(isDropTarget) {
rect.classed("dropTarget", isDropTarget)
}
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r", r)
.attr("transform", d => `translate(${d.x},${d.y})`)
.attr("fill", (_, i) => colors[i])
.call(drag);
function dragCircle(event) {
const [x, y] = d3.pointer(event, svg.node());
d3.select(this)
.attr("transform", `translate(${x},${y})`);
updateRect(overDragTarget(x, y));
}
function overDragTarget(x, y) {
const rectBounds = rect.node().getBBox();
return contains(rectBounds, ({x:x, y:y}));
}
function startDragging(event) {
const isDragging = true;
d3.select(this)
.classed("dragging", isDragging)
.raise();
}
function endDragging(event) {
const isDragging = false
const circle = d3.select(this)
.classed("dragging", isDragging);
updateRect(isDragging);
const [x, y] = d3.pointer(event, svg.node());
if (overDragTarget(x, y)) {
circle
.transition()
.attr("r", r * 1.2)
.transition()
.attr("r", 0);
}
}
return svg.node();
}