Published
Edited
Oct 13, 2021
Fork of Simple D3
1 star
Insert cell
Insert cell
// Make up some random data
data = d3.range(10).map(d => ({ index: d, value: Math.random() * 25}));
Insert cell
Insert cell
viewof currentIndex = Scrubber(
d3.range(0, data.length, 1),
{ autoplay: false, delay: 1000, loop: false }
)
Insert cell
// wire things up so when the Scrubber changes we call the `setIndex` function inside the `chartAnimate` variable below
chartAnimate.setIndex(currentIndex)
Insert cell
chartAnimate = {
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)

const offset = 50;
svg
.selectAll("circle")
.data([data[0]], d => 'current')
.join("circle")
.attr("cx", width / 2) // just put it in the middle of the area
.attr("cy", height / 2)
.attr("r", d => d.value)
.attr("fill", d => colorScale(d.value))

const setIndex = (newIndex) => {
svg
.selectAll("circle")
.data([data[newIndex]], d => 'current')
.transition()
.duration(1000)
.attr("r", d => d.value)
.attr("fill", d => colorScale(d.value))
}
// Extend SVG node, export setYear as a property thereof
return Object.assign(svg.node(), { setIndex });
}
Insert cell
Insert cell
// and lets use some nice colors
colorScale = d3.scaleSequential(d3.interpolateBlues)
.domain(d3.extent(data.map(d => d.value)))
Insert cell
{
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)

const offset = 50;
svg
.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", d => 100 + (d.index * 50)) // this should really be an axis that tells us where to position each circle
.attr("cy", height / 2)
.attr("r", d => d.value)
.attr("fill", d => colorScale(d.value))
return svg.node()
}
Insert cell
Insert cell
// use the `pack` 2d layout algorithm to place circles near each other: https://github.com/d3/d3-hierarchy#pack
packAlgorithm = data => d3.pack() // create a 2d packing layout algorithm
.size([width - 2, height - 2]) // that has this overall size
.padding(3) // and leaves this much space between each node
// now call the algorithm
(d3.hierarchy({children: data}) // now tell it to compute positions with a flat tree (ie. no hierarchy)
.sum(d => d.value)) // and call sum before we use it (because that's just how it works)
Insert cell
// adapted from https://observablehq.com/@d3/bubble-chart
chart = {
const root = packAlgorithm(data); // run the circle packing algorithm to figure out where each circle should go

// set up an SVG container to hold our packed circles
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("font-size", 10) // this font will apply to any text nodes inside this SVG container
.attr("font-family", "sans-serif")
.attr("text-anchor", "middle");

// set up a container for each circle and its label
const leaf = svg.selectAll("g") // a <g> is the generic container to hold stuff in SVG (like <div> in HTML)
.data(root.leaves()) // the data here is all the "leaves" the algorithm has computed for us (1 per data point)
.join("g")
.attr("transform", d => `translate(${d.x + 1},${d.y + 1})`); // put the g container where the algorithm computed (x/y)

// now that it is in the right place, add a circle
leaf.append("circle")
.attr("id", d => (d.leafUid = DOM.uid("leaf")).id) // give it a unique id for good form
.attr("r", d => d.r) // this is the original value WE told it to use
.attr("stroke", d => 'lightgrey') // add a border for better visibility
.attr("fill", d => colorScale(d.value)); // just a normal fill using the colorScale we set up previously

// and now add a label on top of the circle (in the same <g>)
leaf.append("text")
.attr("x", 0) // this x/y position is local to the <circle> element (ie. `leaf`), not to the whole <svg> area
.attr("y", 4)
.text(d => Math.round(d.value));
return svg.node();
}
Insert cell
format = d3.format(",d")
Insert cell
height = 200
Insert cell
import {Scrubber} from "@mbostock/scrubber"
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more