Public
Edited
Mar 28, 2023
Insert cell
Insert cell
viewof i = Scrubber(numbers)
Insert cell
chart = {
// for (let i in data.length){
// const discDataB = _.map(data, 'radB');
const svg = d3.select(DOM.svg(300, 300))
.style("width", "60%")
.style("height", "auto")
.style("font", "4.5px sans-serif")
// .style("text.axis-tick-marks", "7px")
;
svg.append("rect") // background
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "powderblue")
;
svg.append("circle") // circle-A
.attr("r", data[i]['radA'])
.attr("cx", 150)
.attr("cy", 150)
.attr("fill", "white")
.attr("stroke", "ghostwhite")
.attr("fill-opacity", 0.8)
.attr("stroke-opacity", 0.8)
;
svg.append("circle") // circle-B, static
.attr("r", data[i]['radB'])
.attr("cx", 150)
.attr("cy", 150)
.attr("fill", "plum")
;
// svg.append("circle") // circle-B, animated
// .attr("cy", centery)
// .attr("cx", centerx)
// // .attr("r", 25) // works!
// .style("fill", "red")
// .style("fill-opacity", 0.25)
// .attr("id", "bsc")
// ;
// svg // circle-B, animated
// .selectAll("circle")
// .data(discDataB)
// .enter()
// .append("circle")
// .attr("cy", centery)
// .attr("cx", centerx)
// // .attr("r", function(d) {return (d); }) // shows all circles at once, no animation
// .attr('r', function(d, i) {return i; })
// .style("fill", "red")
// .style("fill-opacity", 0.25)
// .attr("id", "bsc")
// ;
svg.append("circle") // circle-C
.attr("r", data[i]['radC'])
.attr("cx", 150)
.attr("cy", 150)
.attr("fill", "black")
;
svg.append("text")
.style("font", "12px Source Serif Pro")
.attr("x", 10)
.attr("y", 20)
.text("Prototype: 2d plan view of 3d form")
;
svg.append("text")
.style("font", "7px Source Serif Pro")
.attr("x", 12)
.attr("y", 30)
.text("visual bootstrap")
;
// function bootstrapIt() {
// svg.selectAll("#bsc")
// .data(discDataB)
// .enter()
// .transition()
// .attr("cx", centerx)
// .attr("cy", centery)
// // .attr("r", function(d) {return (d);} ) // shows all circles at once, no animation
// .attr('r', function(d, i) {return i; })
// // .attr("r", 50) // works!
// ;
// }
// if (animate) {setInterval(bootstrapIt, 500)}
return svg.node();
// }
}
Insert cell
numbers = Array.from({length: data.length}, (_, i) => i)
Insert cell
data.length
Insert cell
d3 = require("d3@5")
Insert cell
_ = require('lodash')
Insert cell
data = [
{
"Year": 2023,
"radA": 100,
"radB": 22,
"radC": 11
},
{
"Year": 2022,
"radA": 100,
"radB": 15,
"radC": 8
},
{
"Year": 2021,
"radA": 100,
"radB": 27,
"radC": 9
},
{
"Year": 2020,
"radA": 100,
"radB": 14,
"radC": 8
},
{
"Year": 2019,
"radA": 100,
"radB": 35,
"radC": 9
},
{
"Year": 2018,
"radA": 100,
"radB": 30,
"radC": 8
},
{
"Year": 2017,
"radA": 100,
"radB": 20,
"radC": 8
},
{
"Year": 2016,
"radA": 100,
"radB": 18,
"radC": 6
},
{
"Year": 2015,
"radA": 100,
"radB": 18,
"radC": 6
},
{
"Year": 2014,
"radA": 100,
"radB": 18,
"radC": 6
},
{
"Year": 2013,
"radA": 100,
"radB": 22,
"radC": 6
},
{
"Year": 2012,
"radA": 100,
"radB": 21,
"radC": 6
},
{
"Year": 2011,
"radA": 100,
"radB": 12,
"radC": 4
},
{
"Year": 2010,
"radA": 100,
"radB": 15,
"radC": 5
},
{
"Year": 2009,
"radA": 100,
"radB": 13,
"radC": 5
},
{
"Year": 2008,
"radA": 100,
"radB": 10,
"radC": 4
},
{
"Year": 2007,
"radA": 100,
"radB": 12,
"radC": 3
},
{
"Year": 2006,
"radA": 100,
"radB": 15,
"radC": 4
},
{
"Year": 2005,
"radA": 100,
"radB": 11,
"radC": 3
},
{
"Year": 2004,
"radA": 100,
"radB": 13,
"radC": 3
},
{
"Year": 2003,
"radA": 100,
"radB": 12,
"radC": 2
},
{
"Year": 2002,
"radA": 100,
"radB": 9,
"radC": 3
},
{
"Year": 2001,
"radA": 100,
"radB": 10,
"radC": 3
},
{
"Year": 2000,
"radA": 100,
"radB": 9,
"radC": 2
},
{
"Year": 1999,
"radA": 100,
"radB": 8,
"radC": 2
},
{
"Year": 1998,
"radA": 100,
"radB": 8,
"radC": 2
},
{
"Year": 1997,
"radA": 100,
"radB": 8,
"radC": 2
},
{
"Year": 1996,
"radA": 100,
"radB": 6,
"radC": 2
},
{
"Year": 1995,
"radA": 100,
"radB": 6,
"radC": 1
},
{
"Year": 1994,
"radA": 100,
"radB": 4,
"radC": 1
}
]
Insert cell
function Scrubber(values, {
format = value => value,
initial = 0,
delay = 200,
autoplay = true,
loop = true,
loopDelay = 100,
alternate = false
} = {}) {
values = Array.from(values);
const form = html`<form style="font: 12px var(--sans-serif); font-variant-numeric: tabular-nums; display: flex; height: 33px; align-items: center;">
<button name=b type=button style="margin-right: 0.4em; width: 5em;"></button>
<label style="display: flex; align-items: center;">
<input name=i type=range min=0 max=${values.length - 1} value=${initial} step=1 style="width: 180px;">
<output name=o style="margin-left: 0.4em;"></output>
</label>
</form>`;
let frame = null;
let timer = null;
let interval = null;
let direction = 1;
function start() {
form.b.textContent = "Pause";
if (delay === null) frame = requestAnimationFrame(tick);
else interval = setInterval(tick, delay);
}
function stop() {
form.b.textContent = "Play";
if (frame !== null) cancelAnimationFrame(frame), frame = null;
if (timer !== null) clearTimeout(timer), timer = null;
if (interval !== null) clearInterval(interval), interval = null;
}
function running() {
return frame !== null || timer !== null || interval !== null;
}
function tick() {
if (form.i.valueAsNumber === (direction > 0 ? values.length - 1 : direction < 0 ? 0 : NaN)) {
if (!loop) return stop();
if (alternate) direction = -direction;
if (loopDelay !== null) {
if (frame !== null) cancelAnimationFrame(frame), frame = null;
if (interval !== null) clearInterval(interval), interval = null;
timer = setTimeout(() => (step(), start()), loopDelay);
return;
}
}
if (delay === null) frame = requestAnimationFrame(tick);
step();
}
function step() {
form.i.valueAsNumber = (form.i.valueAsNumber + direction + values.length) % values.length;
form.i.dispatchEvent(new CustomEvent("input", {bubbles: true}));
}
form.i.oninput = event => {
if (event && event.isTrusted && running()) stop();
form.value = values[form.i.valueAsNumber];
form.o.value = format(form.value, form.i.valueAsNumber, values);
};
form.b.onclick = () => {
if (running()) return stop();
direction = alternate && form.i.valueAsNumber === values.length - 1 ? -1 : 1;
form.i.valueAsNumber = (form.i.valueAsNumber + direction) % values.length;
form.i.dispatchEvent(new CustomEvent("input", {bubbles: true}));
start();
};
form.i.oninput();
if (autoplay) start();
else stop();
Inputs.disposal(form).then(stop);
return form;
}
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