Published
Edited
Dec 20, 2020
6 stars
Insert cell
Insert cell
Insert cell
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(); // SVG elements don't have a z-index so bring the circle to the top
}
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();
}
Insert cell
function contains(rect, point) {
return (
point.x >= rect.x &&
point.y >= rect.y &&
point.x <= rect.x + rect.width &&
point.y <= rect.y + rect.height
);
}
Insert cell
html`<style>
circle.dragging { filter: url(#shadow) }
rect { fill: none; stroke: magenta; stroke-width: 2; }
rect.dropTarget { stroke-width: 4; }
</style>`
Insert cell
height = 400
Insert cell
r = 50
Insert cell
X = width / 3
Insert cell
Y = height / 2
Insert cell
rectSize = r * 3
Insert cell
data = [
[X - r, Y - r],
[X - r, Y + r],
[X + r, Y - r],
[X + r, Y + r]
].map((d, i) => ({ id: i, x: d[0], y: d[1] }))
Insert cell
colors = d3.schemeCategory10
Insert cell
md`## Imports`
Insert cell
d3 = require("d3@6")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more