Public
Edited
Feb 16, 2024
1 fork
15 stars
Insert cell
Insert cell
Insert cell
Insert cell
data = [
{id:"BRA",value:2},
{id:"MOZ",value:8},
{id:"DEU",value:3},
{id:"GEO",value:6},
{id:"CHN",value:7}
]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const chart = d3.select(svg`<svg width="${width}" height="50"></svg>`);
chart.call(axisComp)
yield chart.node()
}

Insert cell
Insert cell
{
const chart = d3.select(html`<canvas width="${width}" height="50"></canvas>`)
const context = chart.node().getContext('2d');
const tickSize = 5;
const ticks = scaleAxis.ticks();
const tickFormat = scaleAxis.tickFormat();
context.beginPath();
// Domain Line
context.moveTo(0, 0);
context.lineTo(width, 0);
// Tick Lines
ticks.forEach(function(d) {
context.moveTo(scaleAxis(d), 0);
context.lineTo(scaleAxis(d), tickSize);
});
context.strokeStyle = "black";
context.stroke();

// Tick Texts
context.textAlign = "center";
context.textBaseline = "top";
ticks.forEach(function(d) {
context.fillText(tickFormat(d), scaleAxis(d), tickSize);
});

yield chart.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const chart = d3.select(svg`<svg width="${width}" height="100"></svg>`);
chart.selectAll('.bars')
.data(data)
.join('rect')
.attr('width',10)
.attr('fill','steelblue')
.attr('height',d=>barScaleY(d.value))
.attr('x',(d,i)=>i*eachBarGroupWidth)
.attr('y',d=>-barScaleY(d.value)+100)
yield chart.node()
}

Insert cell
Insert cell
{
const chart = d3.select(html`<canvas width="${width}" height="100"></canvas>`)
const context = chart.node().getContext('2d');
context.fillStyle = "steelblue";
data.forEach(function(d,i) {
context.fillRect(
eachBarGroupWidth * i, // x
-barScaleY(d.value) + 100, // y
10, // width
barScaleY(d.value) // height
);
});
yield chart.node()
}

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const chart = d3.select(svg`<svg width="${width}" height="100"></svg>`);
const lineLayout = d3.line()
.x((d,i) => i * eachLineX)
.y(d => 100-lineScaleY(d.value))
chart.selectAll('.line')
.data([data])
.join('path')
.attr('d',d=> lineLayout(d))
.attr('fill','none')
.attr('stroke','steelblue')
yield chart.node()
}

Insert cell
Insert cell
{
const chart = d3.select(html`<canvas width="${width}" height="100"></canvas>`)
const context = chart.node().getContext('2d');
const lineLayout = d3.line()
.x((d,i) => i * eachLineX)
.y(d => 100-lineScaleY(d.value))
.context(context)
context.beginPath();
lineLayout(data)
context.strokeStyle = "steelblue";
context.stroke();
yield chart.node()
}

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const chart = d3.select(svg`<svg width="${width}" height="100"></svg>`);
chart.selectAll('.point')
.data(data)
.join('circle')
.attr('fill','steelblue')
.attr('cx',(d,i)=>i*eachScatterX+20)
.attr('cy',(d,i)=>100-scatterScaleY(d.value)+10) // We should also move the axises as well
.attr('r',10)
yield chart.node()
}

Insert cell
Insert cell
{
const chart = d3.select(html`<canvas width="${width}" height="100"></canvas>`)
const context = chart.node().getContext('2d');
context.fillStyle = "steelblue";
data.forEach(function(p,i) {
context.beginPath();
context.fillStyle = "steelblue";
context.arc( i * eachScatterX+20,
100-scatterScaleY(p.value)+10,
10,
0,
2 * Math.PI);
context.fill();
});
yield chart.node()
}

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const chart = d3.select(svg`<svg width="${width}" height="210"></svg>`);
const mapPathSvg = d3.geoPath().projection(mapProjection);
chart.selectAll('.mapPaths')
.data(dataFeatures)
.join('path')
.attr('fill','steelblue')
.attr('d', mapPathSvg)
yield chart.node()
}

Insert cell
Insert cell
{
const chart = d3.select(html`<canvas width="${width}" height="250"></canvas>`)
const context = chart.node().getContext('2d');
const mapPathCanvas = d3.geoPath().projection(mapProjection).context(context);
dataFeatures.forEach(f=>{
context.beginPath();
mapPathCanvas(f)
context.fillStyle='steelBlue';
context.fill();
})
yield chart.node()
}

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const chart = d3.select(svg`<svg width="${width}" height="120"></svg>`);
const g = chart.append('g')
.attr('transform',`translate(${width/2},60)`)
const arc = d3.arc()
.outerRadius(50)
.innerRadius(0)
g.selectAll('.mapPaths')
.data(pieData)
.join('path')
.attr('fill','steelblue')
.attr('d', arc)
yield chart.node()
}

Insert cell
Insert cell
{
const chart = d3.select(html`<canvas width="${width}" height="120"></canvas>`)
const context = chart.node().getContext('2d');
context.translate(width / 2, 60);
const arc = d3.arc()
.outerRadius(50)
.innerRadius(0)
.context(context);
pieData.forEach(function(d, i) {
context.beginPath();
arc(d);
context.fillStyle = 'steelblue';
context.fill();
});
yield chart.node()
}

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof svgHoverOutput={
const chart = d3.select(svg`<svg width="${width}" height="100"></svg>`);
chart.selectAll('.point')
.data(data)
.join('circle')
.attr('fill','steelblue')
.attr('cx',(d,i)=>i*eachScatterX_interactivity+20)
.attr('cy',(d,i)=>100-scatterScale_interactivity(d.value)+10)
.attr('r',10)
.on('mousemove',d=>{
output(d,chart)
})
.on('click',d=>{
output(d,chart)
})
yield chart.node();
}

Insert cell
Insert cell
Insert cell
Insert cell
viewof canvasHoverOutput = {
const container = d3.select(html`<div>

<canvas class="chart-container" width="${width}" height="100"></canvas>
<canvas style="position:absolute" class="hidden-canvas" width="${width}" height="100"></canvas>
</div>
`)
const chart = container.select('.chart-container');
const mainContext = chart.node().getContext('2d');
mainContext.fillStyle = "steelblue";

let colorCounter=1;
let nodeColorMap = {};
const hiddenCanvas = container.select('.hidden-canvas')
// .style('display','none')
const hiddenContext = hiddenCanvas.node().getContext('2d');
draw(mainContext,false);
draw(hiddenContext,true);
function draw(context, hidden) {
data.forEach(function(p,i) {
context.beginPath();
context.arc( i * eachScatterX+20,
100-scatterScaleY(p.value)+10,
10,
0,
2 * Math.PI);
// Set corresponding color
context.fillStyle = "steelBlue";
// Set unique color for hidden canvas
if(hidden){
const color = getColor(colorCounter);
nodeColorMap[color] = p;
colorCounter+=2;
context.fillStyle = color;
}
context.fill();
});
}
function getColor(nextCol){ // generating 256*256*256 unique colors
var ret = [];
ret.push(nextCol & 0xff); // R
ret.push((nextCol & 0xff00) >> 8); // G
ret.push((nextCol & 0xff0000) >> 16); // B
return "rgb(" + ret.join(',') + ")";
}
chart.on('mousemove', processEvent)
.on('click', processEvent)
function processEvent(){
var mouseX = d3.event.layerX || d3.event.offsetX;
var mouseY = d3.event.layerY || d3.event.offsetY;
const col = hiddenContext.getImageData(mouseX, mouseY, 1, 1).data;
var colKey = 'rgb(' + col[0] + ',' + col[1] + ',' + col[2] + ')';
const corrData = nodeColorMap[colKey];
if(corrData){
output(corrData,container)
}
}

yield container.node()
}

Insert cell
Insert cell
Insert cell
{
const chart = d3.select(svg`<svg width="${width}" height="100"></svg>`);
const tip = d3tip().attr('class', 'd3-tip').html(function(d) { return JSON.stringify(d,null,' '); });
chart.call(tip);
chart.selectAll('.point')
.data(data)
.join('circle')
.attr('fill','steelblue')
.attr('cx',(d,i)=>i*eachScatterX_interactivity+20)
.attr('cy',(d,i)=>100-scatterScale_interactivity(d.value)+10)
.attr('r',10)
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
yield chart.node();
}

Insert cell
Insert cell
Insert cell
{
const container = d3.select(html`<div>
<svg class="tooltip-wrapper" width=0 height=0 ></svg>
<canvas class="chart-container" width="${width}" height="100"></canvas>
<canvas style="position:absolute" class="hidden-canvas" width="${width}" height="100"></canvas>
</div>
`)
const svg = container.select('svg');
const tip = d3tip().attr('class', 'd3-tip').html(function(d) { return JSON.stringify(d,null,' '); });
svg.call(tip);
const chart = container.select('.chart-container');
const mainContext = chart.node().getContext('2d');
mainContext.fillStyle = "steelblue";


let colorCounter=1;
let nodeColorMap = {};
const hiddenCanvas = container.select('.hidden-canvas')
// .style('display','none')
const hiddenContext = hiddenCanvas.node().getContext('2d');
draw(mainContext,false);
draw(hiddenContext,true);
function draw(context, hidden) {
data.forEach(function(p,i) {
context.beginPath();
context.arc( i * eachScatterX+20,
100-scatterScaleY(p.value)+10,
10,
0,
2 * Math.PI);
// Set corresponding color
context.fillStyle = "steelBlue";
// Set unique color for hidden canvas
if(hidden){
const color = getColor(colorCounter);
nodeColorMap[color] = p;
colorCounter+=2;
context.fillStyle = color;
}
context.fill();
});
}
function getColor(nextCol){ // generating 256*256*256 unique colors
var ret = [];
ret.push(nextCol & 0xff); // R
ret.push((nextCol & 0xff00) >> 8); // G
ret.push((nextCol & 0xff0000) >> 16); // B
return "rgb(" + ret.join(',') + ")";
}
chart.on('mousemove', processEvent)
function processEvent(){
var mouseX = d3.event.layerX || d3.event.offsetX;
var mouseY = d3.event.layerY || d3.event.offsetY;
const col = hiddenContext.getImageData(mouseX, mouseY, 1, 1).data;
var colKey = 'rgb(' + col[0] + ',' + col[1] + ',' + col[2] + ')';
const corrData = nodeColorMap[colKey];
if(corrData){
tip
.offset([mouseY,mouseX])
.show(corrData,svg.node())
output({corrData},container)
}else{
tip.hide()
}
}

yield container.node()
}

Insert cell
Insert cell
Insert cell
{
const chart = d3.select(svg`<svg width="${width}" height="100"></svg>`);
chart.selectAll('.point')
.data(data)
.join('circle')
.attr('class','point')
.attr('fill','steelblue')
.attr('cx',(d,i)=>i*eachScatterX_interactivity+20)
.attr('cy',(d,i)=>100-scatterScale_interactivity(d.value)+10)
.attr('r',10)
const interval = setInterval(()=>{
chart.selectAll('.point')
.transition()
.duration(1000)
.attr('cx',d=>Math.random()*width)
.attr('cy',d=>Math.random()*100)
},1000)
invalidation.then(d=>clearInterval(interval)) // Observablehq specific code for cleaning things up
yield chart.node();
}

Insert cell
Insert cell
{
const chart = d3.select(html`<canvas width="${width}" width="300" height="100"></canvas>`)
const copiedData = JSON.parse(JSON.stringify(data))
copiedData.forEach((circle,i)=>{
circle.x = i*eachScatterX_interactivity+20;
circle.y = 100-scatterScale_interactivity(circle.value)+10;
})
const context = chart.node().getContext('2d');
context.fillStyle = "steelBlue";
const radius = 10;
var detachedContainer = d3.select(document.createElement("custom"))
const pointsSel = detachedContainer.selectAll('.point')
.data(copiedData)
.join('circle')
.attr('class','point')
.attr('cx',(d,i)=>d.x)

function render(){
context.clearRect(0, 0, width, 100);
detachedContainer
.selectAll('.point')
.each(function(d){
const circle = d;
const node = d3.select(this);
context.beginPath();
context.arc( node.attr('cx'), node.attr('cy'), radius, 0, 2 * Math.PI);
context.fill();
})
}
const interval = setInterval(()=>{
detachedContainer.selectAll('.point')
.transition()
.duration(1000)
.delay((d,i,arr)=>i/arr.length*200)
.attr('cx',d=>Math.random()*width)
.attr('cy',d=>Math.random()*100)
.tween('tick',d=>{
return (t)=>{
render();
}
})
},1200)
invalidation.then(d=>clearInterval(interval)) // Observablehq specific code for cleaning things up
yield chart.node()
}

Insert cell
Insert cell
Insert cell
data
Insert cell
{
const chart = d3.select(svg`<svg width="${width}" height="100"></svg>`);
const g = chart.append("g");

const zoom = d3.zoom().on("zoom", zoomed);
chart.call(zoom);

g.append("rect").attr("width", 20).attr("height", 20);

g.selectAll(".point")
.data(data)
.join("circle")
.attr("class", "point")
.attr("fill", "steelblue")
.attr("cx", (d, i) => i * eachScatterX_interactivity + 20)
.attr("cy", (d, i) => 100 - scatterScale_interactivity(d.value) + 10)
.attr("r", 10);

function zoomed() {
const transform = d3.event.transform;
g.attr("transform", transform);
console.log("zooming");
}

yield chart.node();
}
Insert cell
Insert cell
{
const chart = d3.select(html`<canvas width="${width}" width="300" height="100"></canvas>`)
const zoom = d3.zoom().on("zoom", zoomed)
chart.call(zoom)
const copiedData = JSON.parse(JSON.stringify(data))
copiedData.forEach((circle,i)=>{
circle.x = i*eachScatterX_interactivity+20;
circle.y = 100-scatterScale_interactivity(circle.value)+10;
})
const context = chart.node().getContext('2d');
context.fillStyle = "steelBlue";
const radius = 10;
render();
function render(){
context.clearRect(0, 0, width, 100);
copiedData.forEach(circle=>{
context.beginPath();
context.arc( circle.x, circle.y, radius, 0, 2 * Math.PI);
context.fill();
})
}
function zoomed() {
var transform = d3.event.transform;
context.save();
context.clearRect(0, 0, width, 100);
context.translate(transform.x, transform.y);
context.scale(transform.k, transform.k);
render();
context.restore();
}
yield chart.node()
}

Insert cell
Insert cell
Insert cell
codeBlock = {
const chart = d3.select(svg`<svg width="${width}" height="100"></svg>`);

const copiedData = JSON.parse(JSON.stringify(data));

const circles = chart
.selectAll(".point")
.data(copiedData)
.join("circle")
.attr("fill", "steelblue")
.attr("cx", (d, i) => i * eachScatterX_interactivity + 20)
.attr("cy", (d, i) => 100 - scatterScale_interactivity(d.value) + 10)
.attr("r", 10)
.call(d3.drag().on("drag", dragged));

function dragged() {
d3.event.subject.x = d3.event.x;
d3.event.subject.y = d3.event.y;
d3.select(this)
.attr("cx", (d, i) => d.x)
.attr("cy", (d, i) => d.y);
}

yield chart.node();
}
Insert cell
Insert cell
Insert cell
{
const chart = d3.select(html`<canvas width="${width}" width="300" height="100"></canvas>`)
const copiedData = JSON.parse(JSON.stringify(data))
copiedData.forEach((circle,i)=>{
circle.x = i*eachScatterX_interactivity+20;
circle.y = 100-scatterScale_interactivity(circle.value)+10;
})
const context = chart.node().getContext('2d');
context.fillStyle = "steelBlue";
const radius = 10;
render();
function render(){
context.clearRect(0, 0, width, 100);
copiedData.forEach(circle=>{
// Drawing Circles
context.beginPath();
context.arc( circle.x, circle.y, radius, 0, 2 * Math.PI);
context.fill();
})
}
// Determining whether event happens over circle
function dragsubject() {
for(let i=0;i<copiedData.length;i++){
const circle = copiedData[i];
const x = circle.x - d3.event.x;
const y = circle.y - d3.event.y;
if (x * x + y * y < radius * radius) return circle;
}
}
d3.select(chart.node())
.call(d3.drag()
.subject(dragsubject)
.on("drag", dragged)
.on("drag.render", render)
);
function dragged() {
d3.event.subject.x = d3.event.x;
d3.event.subject.y = d3.event.y;
}
yield chart.node()
}

Insert cell
Insert cell
Insert cell
Insert cell
dragSimulation = simulation => {
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
console.log('dragging',d3.event.x)
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
Insert cell
{
const chart = d3.select(svg`<svg width="${width}" height="100"></svg>`);
const forceData = JSON.parse(JSON.stringify(data));
const links = [
{source:'BRA',target:'MOZ'},
{source:'BRA',target:'DEU'},
{source:'BRA',target:'GEO'},
{source:'BRA',target:'CHN'},
]
const simulation = d3.forceSimulation(forceData)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, 50));
const linkElems = chart.selectAll("line")
.data(links)
.join("line")
.attr('stroke','steelblue')
const nodes = chart.selectAll('circle')
.data(forceData)
.join('circle')
.attr('fill','steelblue')
.attr('r',10)
.call(dragSimulation(simulation));
simulation.on("tick", () => {
linkElems
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);

nodes
.attr("cx", d => d.x)
.attr("cy", d => d.y);
});
yield chart.node()
}

Insert cell
Insert cell
{
const chart = d3.select(html`<canvas width="${width}" width="300" height="100"></canvas>`)
const radius = 10;
const height = 100;
const forceData = JSON.parse(JSON.stringify(data));
const links = [
{source:'BRA',target:'MOZ'},
{source:'BRA',target:'DEU'},
{source:'BRA',target:'GEO'},
{source:'BRA',target:'CHN'},
]
const context = chart.node().getContext('2d');
const canvas = context.canvas;
let transform = d3.zoomIdentity;
const simulation = d3.forceSimulation(forceData)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, 50));
context.fillStyle = "steelBlue";
simulation.on("tick", () => {
render()
});

function render(){
context.save();
context.clearRect(0, 0, width, height);
console.log('ticking')
links.forEach(function(d) {
context.beginPath();
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
context.lineWidth = Math.sqrt(d.value);
context.strokeStyle = '#aaa';
context.stroke();
});
// Draw nodes
forceData.forEach(function(d, i) {
context.beginPath();
context.moveTo(d.x + radius, d.y);
context.arc(d.x, d.y, radius, 0, 2 * Math.PI);
context.fill();
});
context.restore();
}
// Determining whether event happens over circle
function dragsubject() {
for(let i=0;i<forceData.length;i++){
const circle = forceData[i];
const x = circle.x - d3.event.x;
const y = circle.y - d3.event.y;
if (x * x + y * y < radius * radius) return circle;
}
}
d3.select(chart.node())
.call(d3.drag()
.container(canvas)
.subject(dragsubject)
.on('start',dragstarted)
.on("drag", dragged)
.on("drag.render", render)
);
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged() {
d3.event.subject.x = d3.event.x;
d3.event.subject.y = d3.event.y;
}
yield chart.node()
}

Insert cell
Insert cell
Insert cell
brushSvg
Insert cell
viewof brushSvg = {
const chart = d3.select(svg`<svg width="${width}" height="100"></svg>`);
chart.selectAll('.point')
.data(data)
.join('circle')
.attr('fill','steelblue')
.attr('cx',(d,i)=>i*eachScatterX+20)
.attr('cy',(d,i)=>100-scatterScaleY(d.value)+10) // We should also move the axises as well
.attr('r',10)
chart.call(d3.brushX().on('brush',brushed))
function brushed() {
chart.property("value", d3.event.selection);
chart.dispatch("input");
}
return Object.assign(chart.node(),{value:[]})
}

Insert cell
Insert cell
Insert cell
canvasSvg
Insert cell
viewof canvasSvg ={
const chart = d3.select(html`<canvas width="${width}" height="100"></canvas>`)
const svg = d3.create('svg');
const result = d3.select(html`<div style="height:100px">
<div style="position:absolute;top:0">${chart.node()} <div/>
<div style="position:absolute;top:0">${svg.node()} </div>
</div>`);
const context = chart.node().getContext('2d');
context.fillStyle = "steelblue";
data.forEach(function(p,i) {
context.beginPath();
context.fillStyle = "steelblue";
context.arc( i * eachScatterX+20,
100-scatterScaleY(p.value)+10,
10,
0,
2 * Math.PI);
context.fill();
});

svg
.attr('width',width)
.attr('height',100)
.call(d3.brushX().on('brush',(d)=>{
console.log('brushX')
result.property("value", d3.event.selection);
result.dispatch("input");
}))
return Object.assign(result.node(),{value:[]})
}

Insert cell
Insert cell
Insert cell
lassoSvg
Insert cell
viewof lassoSvg = {
const svgNode = d3.select(svg`<svg width=${width} height=100></svg>`)
const circles = svgNode.selectAll("circle")
.data(data)
.join('circle')
.attr('fill','steelblue')
.attr('cx',(d,i)=>i*eachScatterX+20)
.attr('cy',(d,i)=>100-scatterScaleY(d.value)+10) // We should also move the axises as well
.attr('r',10)
// ---------------- LASSO STUFF . ----------------
function output(value){
svgNode.property("value", value);
svgNode.dispatch("input");
}
var lasso_start = function() {
output({
possibleItems:[],
notPossibleItems:lasso.items().data(),
selected:[],
notSelected:[],
})
};

var lasso_draw = function() {
output({
possibleItems:lasso.possibleItems().data(),
notPossibleItems:lasso.notPossibleItems().data(),
selected:[],
notSelected:[],
})
};

var lasso_end = function() {
output({
possibleItems:[],
notPossibleItems:lasso.notPossibleItems().data(),
selected:lasso.selectedItems().data(),
notSelected: lasso.notSelectedItems().data(),
})
};
const lasso = d3.lasso()
.closePathDistance(305)
.closePathSelect(true)
.targetArea(svgNode)
.items(circles)
.on("start",lasso_start)
.on("draw",lasso_draw)
.on("end",lasso_end);

const lassoPath = svgNode.call(lasso);

svgNode.selectAll('.lasso .drawn').style('fill-opacity',0.05)
svgNode.selectAll('.lasso .origin').style('fill-opacity',0.5)
svgNode.selectAll('.lasso .loop_close').style('fill','none').style('stroke-dasharray','4,4')
svgNode.selectAll('.lasso path').style('stroke','rgb(80,80,80)').style('stroke-width',2+'px')
return svgNode.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
d3 = {
window.d3 = d3Import;
return d3Import;
}
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