Published
Edited
Aug 31, 2020
1 fork
5 stars
Insert cell
Insert cell
chart = {
const data = [
{time: new Date("2019-09-23T09:00Z"), target: 70},
{time: new Date("2019-09-23T13:00Z"), target: 75},
{time: new Date("2019-09-23T17:00Z"), target: 85},
{time: new Date("2019-09-23T18:00Z"), target: 65},
{time: new Date("2019-09-23T01:00Z"), target: 90},
{time: new Date("2019-09-23T08:00Z"), target: 65}
];

const drag = d3.drag()
// What does the .subject function do?
// Compute the correct offset - HOW
.subject(d => ({x: x(d.time)}))
// .on event perform function
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);

// create the canvas for your d3 work
const svg = d3.create("svg")
// what's a viewBox? Is that an Observable thing?
.attr("viewBox", [0, 0, width, height])
.on("click", bgclicked);

svg.append("g")
// add the xAxis specs to "g"
.call(xAxis);

// Create a group element for our data.
// When we selectAll("g"), we don’t want to re-select our axis!
const datalayer = svg.append("g")
.call(join);

// Call this whenever the data changes.
function join(g) {
g.selectAll("g")
.data(data, d => d.time) // Use a key to bind consistently.
.join(enter, update, exit);
}

// This is called for new data that is added.
function enter(enter) {
return enter.append("g")
.attr("transform", d => `translate(${x(d.time)},0)`)
.call(g => g.append("circle")
.attr("cy", 55)
.attr("r", 0)
.attr("fill", "#1D1A34")
.transition()
.attr("r", 30))
.call(g => g.append("text")
.attr("y", 60)
.attr("fill", "white")
.attr("text-anchor", "middle")
.text(d => d.target))
.on("click", clicked)
.on("dblclick", dblclicked)
.call(drag);
}

// This gets called for old data that is changed (e.g., dragging).
// All we need to do is translate the element into the new position.
// (This assumes that only the “time”, and not “target”, can change.)
function update(update) {
return update
.attr("transform", d => `translate(${x(d.time)},0)`);
}

// This is called for old data that is removed.
function exit(exit) {
return exit.transition()
.call(g => g.select("circle").attr("r", 0))
.remove();
}

// Remove a data point when it is double-clicked.
function dblclicked(d) {
data.splice(data.indexOf(d), 1);
datalayer.call(join);
}

// Clicking on a circle shouldn’t insert a new circle.
function clicked() {
d3.event.stopPropagation();
}

// Insert a new data point when the background is clicked.
function bgclicked() {
const [mx] = d3.mouse(this);
data.push({time: x.invert(mx), target: 0});
datalayer.call(join);
}

// Note: raising an element on drag time can prevent a click, but since raise is a
// no-op when the element is already raised, this does not interfere with dblclick!
function dragstarted(d) {
d3.select(this).attr("stroke", "#FCE584")
data.splice(data.indexOf(d), 1), data.push(d); // Raise the dragged element.
datalayer.call(join);
}

function dragged(d) {
d.time = x.invert(d3.event.x);
datalayer.call(join);
}

function dragended(d) {
d3.select(this).attr("stroke", null);
}

return svg.node();
}
Insert cell
x = d3.scaleTime()
.domain([new Date("2019-09-23T00:00"), new Date(new Date("2019-09-23T24:00Z"))])
.rangeRound([margin.left, width - margin.right]);
Insert cell
interval = d3.timeHour.every(1)
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(g => g.append("g")
.call(d3.axisBottom(x)
.ticks(interval)
.tickSize(-height + margin.top + margin.bottom)
.tickFormat(() => null))
.call(g => g.select(".domain")
.attr("fill", "#FB734B")
.attr("stroke", null))
.call(g => g.selectAll(".tick line")
.attr("stroke", "#FCE584")
.attr("stroke-opacity", d => d <= d3.timeHour(d) ? 1 : 0.5)))
.call(g => g.append("g")
.call(d3.axisBottom(x)
.ticks(d3.timeHour)
.tickPadding(0))
.attr("text-anchor", null)
.call(g => g.select(".domain").remove())
.call(g => g.selectAll("text").attr("x", 6)))
Insert cell
margin = ({top: 10, right: 0, bottom: 20, left: 0})
Insert cell
height = 120
Insert cell
d3 = require("d3@5")
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