Published
Edited
Aug 27, 2020
18 forks
44 stars
Insert cell
Insert cell
Insert cell
chart = {
let selected = mutable points[0];

const svg = d3.create("svg")
.attr("viewBox", [-14, 0, width + 28, height])
.attr("tabindex", 1)
.attr("pointer-events", "all")
.call(d3.drag()
.subject(dragsubject)
.on("start", dragstarted)
.on("drag", dragged));

svg.append("style").text(`
svg[tabindex] {
display: block;
margin: 0 -14px;
border: solid 2px transparent;
box-sizing: border-box;
}
svg[tabindex]:focus {
outline: none;
border: solid 2px lightblue;
}
`);

svg.append("rect")
.attr("fill", "none")
.attr("width", width)
.attr("height", height);

svg.append("path")
.datum(mutable points)
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.call(update);

d3.select(window)
.on("keydown", keydown);

function update() {
svg.select("path").attr("d", line);

const circle = svg.selectAll("g")
.data(mutable points, d => d)

circle.enter().append("g")
.call(g => g.append("circle")
.attr("r", 30)
.attr("fill", "none"))
.call(g => g.append("circle")
.attr("r", 0)
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.transition()
.duration(750)
.ease(d3.easeElastic)
.attr("r", 5))
.merge(circle)
.attr("transform", d => `translate(${d})`)
.select("circle:last-child")
.attr("fill", d => d === selected ? "lightblue" : "black");

circle.exit().remove();
}

function dragsubject(event) {
let subject = event.sourceEvent.target.__data__;
if (!subject) {
mutable points.push(subject = [event.x, event.y]);
mutable points = mutable points;
update();
}
return subject;
}

function dragstarted({subject}) {
selected = subject;
update();
}

function dragged(event) {
event.subject[0] = Math.max(-14, Math.min(width + 14, event.x));
event.subject[1] = Math.max(0, Math.min(height, event.y));
mutable points = mutable points;
update();
}

function keydown(event) {
if (!selected) return;
switch (event.key) {
case "Backspace":
case "Delete": {
event.preventDefault();
const i = mutable points.indexOf(selected);
mutable points.splice(i, 1);
mutable points = mutable points;
selected = mutable points.length ? mutable points[i > 0 ? i - 1 : 0] : null;
update();
break;
}
}
}

return svg.node();
}
Insert cell
mutable points = d3.range(1, 5).map(i => [i * width / 5, 50 + Math.random() * (height - 100)])
Insert cell
height = 600
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