Published
Edited
Jan 26, 2022
15 stars
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
Insert cell
// Slider with title and numeric value, to be used with synchronized views
slider = ({ min, max, step, view, title }) =>
html`<form class="sync">
<div>${title}</div>
${view.bind(html`<input
type=range
min="${min}"
max="${max}"
step="${step}"
>`)}
${view.bind(html`<input
type="number"
readonly
class="readonly"
>`)}
</form>`
Insert cell
viewof radius = new View(2)
Insert cell
viewof dimension = new View(1)
Insert cell
Insert cell
w = Math.min(640, width)
Insert cell
color = d3.interpolateRdBu
Insert cell
data = getPoints(dimension, radius)
Insert cell
Insert cell
volumeOverRadius = [
[0, 0],
...d3
.pairs(d3.range(0, radius + 0.11, 0.1))
.map(([r0, r1]) => [
r0 / radius,
(volume(r1, dimension) - volume(r0, dimension)) /
volume(radius, dimension)
])
]
Insert cell
groupedPoints = d3.groups(
data.map(d => distance(d) / radius).filter(d => d <= 1),
d => d
)
Insert cell
Insert cell
// For when you may get very close to 0 and 100%, and the distinction remains important, this shows percentages with just enough precision to distinguish them from those extremes
format = n => {
const f = d3.format(".0%")(n);
const p = n * 100;
const closenessTo1 = Math.abs(Math.floor(Math.log10(100 - p)));
const closenessTo0 = Math.abs(Math.floor(Math.log10(p)));
return n === 0 || n === 1
? f
: f === "100%"
? p.toFixed(Math.min(closenessTo1, 100)) + "%"
: f === "0%"
? p.toFixed(Math.min(closenessTo0, 100)) + "%"
: f;
}
Insert cell
r = d => d * 20
Insert cell
arc = d3.arc()({
innerRadius: r(range[0]),
outerRadius: r(range[1]),
startAngle: 0,
endAngle: 2 * Math.PI
})
Insert cell
circleRange = svg`<svg
width="${r(1) * 2}"
height="${r(1) * 2}"
style="margin: 5px;"
>
<g transform="translate(${r(1)}, ${r(1)})">
<circle r="${r(1)}" fill="hsl(0, 0%, 80%)" />
<path d="${arc}" fill="#3b99fc" stroke="white"/>
</g>
</svg>`
Insert cell
tableData = [...d3.range(1, 10), ...d3.range(10, 101, 10)].map(dim => [
dim,
(volume(range[1], dim) - volume(range[0], dim)) / volume(1, dim)
])
Insert cell
html`<style>
input.readonly {
border: none;
padding: none;
font-family: sans-serif;
font-size: inherit;
width: 2em;
pointer-events: none;
}
form.sync {
font-family: sans-serif;
font-size: 12px;
display: flex;
align-items: center;
}
form.sync div:first-child {
margin-right: 1em;
width: 60px;
}
form.sync div:last-child {
margin-left: 1em;
}
form.sync input {
width: 200px;
}
</style>`
Insert cell
// Header illustration
getIllo = () => {
const w = width + 28,
h = 250;
const context = DOM.context2d(w, h, 1);
const getRandomColor = () => `hsl(${Math.random() * 360},50%,50%)`;

context.canvas.style.margin = "0 -14px 0 -14px";
context.canvas.style.position = "relative";
context.canvas.style.zIndex = "-1";
context.canvas.style.imageRendering = "pixelated";
context.globalCompositeOperation = "xor";

const data = d3.range(3).map(d => ({
cx: d3.randomUniform(w * 0.7, w * 0.9)(),
cy: d3.randomUniform(h * 0.2, h * 0.8)(),
r: (Math.random() * h) / 6,
c: getRandomColor(),
t0: 0
}));

let dr = 4;
d3.timer(t => {
if (t > 2000) return true;
data.forEach(d => {
if (t < d.t0) return;
context.strokeStyle = d.c;
context.beginPath();
context.arc(d.cx, d.cy, d.r, 0, Math.PI * 2);
context.stroke();
d.r += dr;
dr *= 0.95;
});
});

return context.canvas;
}
Insert cell
Insert cell
import {distance, getPoints, volume} from "@tophtucker/theres-plenty-of-room-in-the-corners"
Insert cell
import { View } from "@mbostock/synchronized-views"
Insert cell
import {tweet} from "@mbostock/tweet"
Insert cell
import { legend, ramp } from "@d3/color-legend@704"
Insert cell
import { rangeInput } from "@mootari/range-slider"
Insert cell
d3 = require("d3")
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