Public
Edited
Aug 22, 2024
1 star
Insert cell
Insert cell
viewof button = Inputs.button("Click me")
Insert cell
Insert cell
<svg id="root" width="1000" height="1000"/>
Insert cell
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
Insert cell
function createData() {
const n = getRandomInt(10) + 1
const data = []
for(let i = 0; i < n; i++) {
data.push({ val: getRandomInt(250) + 1, key : "Key" + i, x : getRandomInt(500), y: getRandomInt(500)})
}
return data
}
Insert cell
function redraw() {
const t = d3.transition().duration(2000)
const allGs = d3.select("#root")
.selectAll(".edge")
.data(createData())
.join(
// ### enter ### called for all (not-yet existing) elements that got new data
enter => {
// on enter, create a g (group) SVG element
const newGs = enter.append("g").classed("element", true)

// for each g, create a circle with class ".circ"
// append, insert and select propagate their data to the child
// thus, the circle has the same datum as the g
const newCircles = newGs.append("circle")
.classed("circ", true)
.attr("stroke", "#000000")
.attr("stroke-width", 2)
.attr("fill", "#000000") // we directly start in black ...
// set everything that should be set immediatly before the transition() call
// everything after the call well be tweened, i.e., animated to the target value
.transition(t)
.attr("fill", "#FF0000") // ... and become red 2 seconds after creation
// for each g, create a text with class ".keytext"
// append, insert and select propagate their data to the child
// thus, the text has the same datum as the g
// text, x, and y only have to set once (on creation), thus we do this here
const newKeyTexts = newGs.append("text").classed("keytext", true).text(d => d.key + ": ").attr("x", 0).attr("y", 6)

// for each g, create a text with class ".valuetext"
// append, insert and select propagate their data to the child
// thus, the text has the same datum as the g
// x, and y only have to set once (on creation), thus we do this here
// text can change, thus we do not set it in the "enter" part, but in the "all (enter + update)" part
const newValTexts = newGs.append("text").classed("valuetext", true).attr("x", 0).attr("y", 20)
return newGs
},
// ### update ### called for all elements that received new data
update => {
// We only want to re-color the elements when they receive new data
// Alternative All "surviving" elements receive the same color
// "hsl(" + Math.random() * 360 + ",100%,50%)" is calculated once
// update.select(".circ").transition(t).attr("fill", "hsl(" + Math.random() * 360 + ",100%,50%)")
// If we want to give each surviving element its own color, we have to write the value as a function (see the d => in the beginning). This function is evaluated once for each element / datum d.
update.select(".circ").transition(t).attr("fill", d => "hsl(" + Math.random() * 360 + ",100%,50%)")
return update
},
// ### exit ### called for all elements that lost their data
exit => {
// transition all circles to become 0-radius, black and fully opaque
exit.select(".circ")
.transition(t)
.attr("r", 0)
.attr("fill", "#000000")
.attr("fill-opacity", 1)

// exit all gs to the top-right corner, after that remove the gs
// typically the last method call in the exit part is "remove"
return exit.transition(t)
.attr("transform", "translate(1000, 0)")
.remove()
}
)

// ### all ### called for all elements that either entered or updated
// all elements, new and updated, should move to their target location
allGs
.transition(t)
.attr("transform", d => `translate(${100 + d.x}, ${100 + d.y})`)
// all circles of all elements, new and updated, should transition their radius and opacity value to their target values
allGs
.select(".circ")
.transition(t)
.attr("r", d => d.val / 2)
.attr("fill-opacity", 0.2)

// all value texts of all elements, new and updated, should change their text to their target values
allGs
.select(".valuetext")
.text(d => d.val)
}
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