Published
Edited
May 27, 2022
Insert cell
Insert cell
Insert cell
chart = {
const MIN_WIDTH_FOR_LABELS = 30;
const DEFAULT_OPACITY = '0.7';
const OPACITY_ON_OVER = '1';
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);

const rect = svg.append("g")
.selectAll("g")
.data(series)
.join("g")
;
rect.selectAll("rect")
.data(d => d)
.join("rect")
.attr("x", d => x(d[0]))
.attr("y", (d, i) => y(d.data.name))
.attr("width", d => x(d[1]) - x(d[0]))
.attr("height", y.bandwidth())
.attr("fill", d => color(d.key))
.attr("opacity", DEFAULT_OPACITY)
.on('mouseover', (d, i) => {
tooltip
.html(
`<div>${d3.format('.3s')(d.data[d.key].v)} ${d.data[d.key].c}</div>`
)
.style('visibility', 'visible');
d3.select(this).transition()
.attr("opacity", OPACITY_ON_OVER);
})
.on('mousemove', () => {
tooltip
.style('top', d3.event.pageY - 10 + 'px')
.style('left', d3.event.pageX + 10 + 'px');
})
.on('mouseout', () => {
tooltip.html(``).style('visibility', 'hidden');
d3.select(this).transition().attr("opacity", DEFAULT_OPACITY);
});
// text inside the box
rect.selectAll("text")
.data(d => d)
.join("text")
.attr('font-family','sans-serif')
.attr('font-size', '0.9em')
.attr("x", d => x(d[0]) + 5)
.attr("y", (d, i) => y(d.data.name) + 20)
.attr("fill", 'black')
.append('tspan')
.attr("x", d => x(d[0]) + 5)
.attr("dx", 0)
.attr("dy", 0)
.attr('font-size', '0.8em')
.text(d => x(d[1]) - x(d[0]) > MIN_WIDTH_FOR_LABELS ? `${d3.format('.3s')(d.data[d.key].v)}` : '')
.append('tspan')
.attr('font-size', '0.8em')
.text(d => x(d[1]) - x(d[0]) > MIN_WIDTH_FOR_LABELS ? ` ${d.data[d.key].c}` : '')
;

svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);

return svg.node();
}
Insert cell
async function getSketchData(data) {
var url = "https://openprocessing.org/api/sketch/"+data.visualID+"/true";
return await fetch(url)
.then(response => {
return response.clone().json()
})
.then(async function(json){
Object.assign(data, json.sketch);
return data;
}).catch(function(error) {
console.error("GET SKETCH: " + data.visualID + " - " + error);
return data;
});
}

Insert cell
async function getUserData(userID) {
var url = "https://openprocessing.org/api/user/"+userID+"/true";
return await fetch(url)
.then(response => {
return response.json()
})
.then(async function(json){
for(var data of json.user.visuals){
await getSketchData(data).then(d => data = d)
}
return json.user;
}).catch(function(error) {
console.error("GET USER: " + userID + " - " + error);
return null;
});
}
Insert cell
//user = getUserData("65884")
Insert cell
user = FileAttachment("user@2.json").json()
Insert cell
function parseUserVisualsToData(){
return user.visuals.map(v => {
return {
name: v.title + " (" + v.visualID + ")",
views: { c: "views", v: v.numberOfViews ? parseInt(v.numberOfViews) : 0},
comments: { c: "comments", v: v.comments ? v.comments.length : 0},
hearts: { c: "hearts", v: v.hearts ? v.hearts.length: 0},
curations: { c: "curations", v: v.curations ? v.curations.length : 0},
}
}).sort((a, b) =>
- a.views.v - a.comments.v - a.hearts.v - a.curations.v
+ b.views.v + b.comments.v + b.hearts.v + b.curations.v
);
}
Insert cell
data = parseUserVisualsToData();
Insert cell
series = d3.stack()
.keys(Object.keys(data[0]).filter((k) => k !== 'name')) // get keys automatically
.value((d,k) => d[k].v)
(data)
.map(d => (d.forEach(v => v.key = d.key), d))
Insert cell
x = d3.scaleLinear()
.domain([0, d3.max(series, d => d3.max(d, d => d[1]))])
.range([margin.left, width - margin.right])
Insert cell
y = d3.scaleBand()
.domain(data.map(d => d.name))
.range([margin.top, height - margin.bottom])
.padding(0.08)
Insert cell
color = d3.scaleOrdinal()
.domain(series.map(d => d.key))
.range(d3.schemeSpectral[series.length])
.unknown("#ccc")
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${margin.top})`)
.call(d3.axisTop(x).ticks(width / 100, "s").tickSize(-(height - margin.top - margin.bottom)))
.call(g => g.selectAll(".domain").remove())
.call(g => g.selectAll(".tick:not(:first-of-type) line")
.attr("stroke-opacity", 0.3)
.attr("stroke-dasharray", "2,2"))
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).tickSizeOuter(0))
.call(g => g.selectAll(".domain").remove())
Insert cell
formatValue = x => isNaN(x) ? "N/A" : x.toLocaleString("en")
Insert cell
height = data.length * 30 + margin.top + margin.bottom
Insert cell
margin = ({top: 30, right: 10, bottom: 0, left: 150})
Insert cell
tooltip = d3
.select('body')
.append('div')
.attr('class', 'd3-tooltip')
.style('font-family','sans-serif')
.style('font-size','0.8em')
.style('position', 'absolute')
.style('z-index', '10')
.style('visibility', 'hidden')
.style('padding', '10px')
.style('background', 'rgba(0,0,0,0.6)')
.style('border-radius', '4px')
.style('color', '#fff')
.text('');
Insert cell
d3 = require("d3@5")
Insert cell
import {legend} from "@d3/color-legend"
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