Public
Edited
Feb 21
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
updateSvgD3 = {
// creating a copy of each datum (object) to avoid overwriting values used by other renderers
const D3Data = data.map((d) => ({ ...d }));
const container = d3.select(svg);

const circleUpdate = container
.selectAll("circle")
.data(D3Data, (d) => d.id)
.attr("fill", fill)
.call((update) =>
update
.transition()
.duration(duration)
.ease(d3.easeQuadInOut)
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y)
);

const circleEnter = circleUpdate
.enter()
.append("circle")
.attr("r", radius)
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y);

return "D3 rendered to SVG Code";
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
updateCanvasD3 = {
// D3 rendered to Canvas
// based on: https://bocoup.com/blog/d3js-and-canvas

const canvasD3Data = data.map((d) => ({ ...d }));

// Create a d3 selection for the detached container.
// We won't actually be attaching it to the DOM.
const dataContainer = d3.select(detachedContainer);
const canvas = d3.select("canvas.d3").node();
const context = canvas.getContext("2d");

// update and enter from the general pattern just like SVG example above
const circleUpdate = dataContainer
.selectAll("circle")
.data(canvasD3Data, (d) => d.id)
.attr("fill", fill)
.call((update) =>
update
.transition()
.duration(duration)
.ease(d3.easeQuadInOut)
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y)
);

const circleEnter = circleUpdate
.enter()
.append("circle")
.attr("r", radius)
.attr("cx", (d) => d.x)
.attr("cy", (d) => d.y);

function drawCanvas() {
// clear canvas
context.fillStyle = "#fff";
context.rect(0, 0, canvas.width, canvas.height);
context.fill();

// select our dummy nodes and draw the data to canvas.
dataContainer.selectAll("circle").each(function (d) {
var node = d3.select(this);
context.beginPath();
context.fillStyle = node.attr("fill");
context.arc(
node.attr("cx"),
node.attr("cy"),
node.attr("r"),
0,
2 * Math.PI
);
context.fill();
context.closePath();
});
}

d3.timer(drawCanvas);
return "D3 rendered to Canvas Code";
}
Insert cell
Insert cell
Insert cell
Insert cell
updateCanvasVanilla = {
// Update Canvas with Vanilla JS
const vanillaData = data.map((d) => ({ ...d }));
const canvas = d3.select("canvas.js").node();
const context = canvas.getContext("2d");

function drawCanvas() {
// clear canvas
context.fillStyle = "#fff";
context.rect(0, 0, canvas.width, canvas.height);
context.fill();

// draw the data to canvas.
vanillaData.forEach((d) => {
context.beginPath();
context.fillStyle = fill;
context.arc(d.cx, d.cy, radius, 0, 2 * Math.PI);
context.fill();
context.closePath();
});
}

function animate(time) {
if (start === undefined) {
start = time;
}
// calculate how far we are into the transition (t)
const elapsed = time - start;
const t = Math.max(0, Math.min(1, d3.easeQuadInOut(elapsed / duration)));
vanillaData.forEach((d) => {
d.cx = d.sx * (1 - t) + d.x * t;
d.cy = d.sy * (1 - t) + d.y * t;
});
// render
drawCanvas();
if (elapsed < duration) {
// draw another frame if the animation is still going
requestAnimationFrame(animate);
}
}

let start; // reset start each time data is updated
requestAnimationFrame(animate);

return "Vanilla JS rendered to Canvas Code";
}
Insert cell
Insert cell
three = {
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
invalidation.then(() => renderer.dispose());
renderer.setSize(width, height);
renderer.setPixelRatio(devicePixelRatio);
renderer.setClearColor( 0x000000, 0 );
renderer.autoClear = false;
while (true) {
renderer.render(scene, camera);
yield renderer.domElement;
}
}
Insert cell
Insert cell
Insert cell
Insert cell
{
// Update Canvas with THREE JS
const threeData = data.map((d) => ({ ...d }));

// temp obj to do matrix operations on
const dummyObj = new THREE.Object3D();

function animate(time) {
if (start === undefined) {
start = time;
}
// calculate how far we are into the transition (t)
const elapsed = time - start;
const scale = 0.01;
const t = Math.max(0, Math.min(1, d3.easeQuadInOut(elapsed / duration)));

threeData.forEach((d, i) => {
d.cx = d.sx * (1 - t) + d.x * t;
d.cy = d.sy * (1 - t) + d.y * t;
// convert pixels to three.js space
const normalized = {
x: -4 + (d.cx / width) * 8,
y: 2 + (d.cy / height) * -4
};
dummyObj.position.set(normalized.x, normalized.y, 0);
dummyObj.scale.set(scale, scale, scale);
dummyObj.updateMatrix();
// update instanced point
points.setMatrixAt(i, dummyObj.matrix);
});
points.instanceMatrix.needsUpdate = true;

if (elapsed < duration) {
// draw another frame if the animation is still going
requestAnimationFrame(animate);
}
}

let start; // reset start each time data is updated
requestAnimationFrame(animate);

return "THREE JS rendered to Canvas Code";
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
sprites = {
const sprites = [];
const particleContainer = new PIXI.ParticleContainer(particles, {
position: true
});
for (let i = 0; i < particles; i++) {
const sprite = new PIXI.Sprite(texture);
sprite.x = Math.random() * width;
sprite.y = Math.random() * height;
particleContainer.addChild(sprite);
sprites.push(sprite);
}
app.stage.addChild(particleContainer);
return sprites;
}
Insert cell
{
// Update Canvas with PIXI JS
const pixiData = data.map((d) => ({ ...d }));

function animate(time) {
if (start === undefined) {
start = time;
}
// calculate how far we are into the transition (t)
const elapsed = time - start;
const t = Math.max(0, Math.min(1, d3.easeQuadInOut(elapsed / duration)));

pixiData.forEach((d, i) => {
d.cx = d.sx * (1 - t) + d.x * t;
d.cy = d.sy * (1 - t) + d.y * t;
const sprite = sprites[i];
if (sprite) {
sprite.x = d.cx;
sprite.y = d.cy;
}
});

if (elapsed < duration) {
// draw another frame if the animation is still going
requestAnimationFrame(animate);
}
}

let start; // reset start each time data is updated
requestAnimationFrame(animate);

return "PIXI JS rendered to Canvas Code";
}
Insert cell
Insert cell
Insert cell
startData = new Array(particles)
.fill({
x: width / 2,
y: height / 2
})
.map((d, i) => {
const id = `c-${i}`;
return {
...d,
sx: d.x,
sy: d.y,
id
};
})
Insert cell
Insert cell
data = {
let nextData = startData;
while (true) {
yield Promises.tick(
duration,
nextData.map((d) => {
// update source position
d.sx = d.x;
d.sy = d.y;
// update destination position
d.x = Math.random() * width;
d.y = Math.random() * height;
return d;
})
);
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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