Published
Edited
Oct 28, 2021
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const width = 500;
const height = 100;

const svg = d3.create("svg")
.attr("viewbox", [0, 0, width, height])
.style("width", `${width}px`)
.style("height", `${height}px`)
.style("background", "#222");


const ghost = svg.append("image")
.attr("xlink:href", ghostImg)
.attr("x", 25)
.attr("y", 25)
.attr("width", 75)
.attr("height", 75);

// add a transition here
return svg.node()
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
data = [ 'A', 'B', 'C']
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
// This functionality is borrowed from https://observablehq.com/@d3/selection-join?collection=@d3/d3-selection
const t = svg.transition()
.duration(750);

svg.selectAll("text")
.data(data, d => d)
.join(
enter => enter.append("text")
.attr("fill", "green")
.attr("x", (d, i) => i * 16)
.attr("y", -30)
.text(d => d)
.call(enter => enter.transition(t)
.attr("y", 0)),
update => update
.attr("fill", "black")
.attr("y", 0)
.call(update => update.transition(t)
.attr("x", (d, i) => i * 16)),
exit => exit
.attr("fill", "brown")
.call(exit => exit.transition(t)
.attr("y", 30)
.remove())
);
return md`The function handling the updates`
}
Insert cell
Insert cell
Insert cell
{
let ghosts = [];
const svg = d3.create("svg")
.attr("viewbox", [0, 0, WIDTH, HEIGHT])
.style("width", `${WIDTH}px`)
.style("height", `${HEIGHT}px`)
.style("background", "#222");


while (toggle){ // loop while the toggle button is set to running
ghosts = retire(ghosts); //remove ghosts that are at the edge
ghosts.forEach(walk); // move the ghosts around
if (ghosts.length <2 || randint(0, 3) < 1){
// add a new ghost
const newGhost = makeGhost(ghosts); // create a new ghost object
ghosts.forEach((g)=> runaway(g, newGhost)); // all of the ghosts run away from the new ghost
ghosts.push(newGhost); //add the new ghost to the collection
}

// use selection and join to display the ghosts
animate(svg, ghosts);

// allow the svg to redraw and then wait for a second
yield svg.node();
await Promises.tick(1000);
}
return svg.node()
}
Insert cell
animate = function(svg, ghosts){

}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
makeGhost = function(ghosts){
const maxId = ghosts.reduce((currentMax, g) => Math.max(currentMax, g.id), -1);
const newGhost = {id: maxId + 1,
x : randint(0, WIDTH-GHOST_SIZE),
y: randint(0, HEIGHT-GHOST_SIZE)};
return newGhost;
}
Insert cell
walk = function(ghost){
ghost.x += randint(10,50) * (-1)**randint(0,1);
ghost.y += randint(10,50) * (-1)**randint(0,1);

ghost.x = ghost.x < 0 ? 0 : ghost.x;
ghost.x = ghost.x > (WIDTH - GHOST_SIZE) ? WIDTH - GHOST_SIZE : ghost.x;
ghost.y = ghost.y < 0 ? 0 : ghost.y;
ghost.y = ghost.y > (HEIGHT - GHOST_SIZE) ? HEIGHT - GHOST_SIZE : ghost.y;

}
Insert cell
runaway = function(ghost, newGhost){
const deltaX = ghost.x - newGhost.x;
const deltaY = ghost.y - newGhost.y;
const speed = randint(10,100) / Math.sqrt(deltaX **2, deltaY ** 2);

ghost.x += deltaX * speed;
ghost.x = ghost.x < 0 ? 0 : ghost.x;
ghost.x = ghost.x > (WIDTH - GHOST_SIZE) ? WIDTH - GHOST_SIZE : ghost.x;
ghost.y += deltaY * speed;
ghost.y = ghost.y < 0 ? 0 : ghost.y;
ghost.y = ghost.y > (HEIGHT - GHOST_SIZE) ? HEIGHT - GHOST_SIZE : ghost.y;
}
Insert cell
retire = function(ghosts){

return ghosts.filter((g)=> g.x > 0 && g.x < WIDTH-GHOST_SIZE && g.y > 0 && g.y < HEIGHT-GHOST_SIZE);
}
Insert cell
d3 = require("d3@7")
Insert cell
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