Published
Edited
Feb 13, 2021
11 stars
Insert cell
Insert cell
Insert cell
// default styles for our div
html`<style>
.painted{
background: paint(${name});
--rotate: [0, 35];
--sphere-color: lightblue;
}`
Insert cell
// updates our div’s styles
animateCss = () =>
d3
.selectAll(".painted")
.interrupt()
.transition()
.ease(d3.easeBackInOut)
.duration(1000)
.style("--rotate", "[720, -15]")
.style("--sphere-color", d3.interpolateRainbow(Math.random()))
.transition()
.duration(5000)
.ease(d3.easeElasticInOut)
.style("--rotate", "[0, -35]")
.style("--sphere-color", "lightblue")
.transition()
.duration(100)
.style("--rotate", "[0, -35]")
Insert cell
// set the click handler on our div, an animate it once
d3
.select(text)
.selectAll(".painted")
.on("click", animateCss)
.style("cursor", "pointer")
.call(animateCss)
Insert cell
// the function we’ll send to the worklet. It only has access to the scripts defined in the preamble
painter = (name, { land }) => {
const projection = d3.geoOrthographic();
const path = d3.geoPath(projection);

let i = 0;

class Worklet {
static get inputProperties() {
return ['--sphere-color', '--rotate'];
}

paint(context, { width, height }, properties) {
console.log("running", i++);

path.context(context);
const rotate = JSON.parse(
properties
.get('--rotate')
.toString()
.trim()
);

projection
.fitExtent([[10, 10], [width - 10, height - 10]], { type: "Sphere" })
.rotate(rotate);

context.fillStyle = properties
.get('--sphere-color')
.toString()
.trim();
context.beginPath();
path({ type: "Sphere" });
context.fill();

context.beginPath();
path(land);
context.fillStyle = "white";
context.fill();

context.beginPath();
path({ type: "Sphere" });
context.stroke();
}
}

globalThis.registerPaint(name, Worklet);
}
Insert cell
// this should be d3-geo@2 when it's compatible, see https://github.com/d3/d3-delaunay/issues/111
preamble = `
import 'https://unpkg.com/d3@6';
`
Insert cell
name = register(preamble, {land})
Insert cell
register = (preamble = "", ...args) => {
const name = "aa" + Math.floor(Math.random() * 1e19);

CSS.paintWorklet.addModule(
"data:text/javascript;base64," +
btoa(
`${preamble};
(${painter.toString()})
(${JSON.stringify(name)},
${args.map(d => JSON.stringify(d))})
`
)
);
return name;
}
Insert cell
land = fetch(
"https://unpkg.com/visionscarto-world-atlas@0.0.6/world/110m_land.geojson"
).then(d => d.json())
Insert cell
Insert cell
// no d3-geo!
d3 = require("d3-selection@2", "d3-transition@2", "d3-ease@2", "d3-scale-chromatic@2")
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