Public
Edited
Mar 3, 2022
3 stars
Insert cell
Insert cell
{
const svgBackgroundColor = "#163C3C",
fontFamily = 'helvetica',

svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.style("background-color", svgBackgroundColor),

bigContainer = svg.append("g")
.attr("transform", `translate(${width/2},${height/2})`),
container = bigContainer.append("g"),
circlesContainer = bigContainer.append("g");

function update(data,concentricData,count){
const t = d3.transition()
.duration(1500);
let labels = container
.selectAll("g")
.data(data, d=>d.bar);
labels.join(
enter => {
let root = enter.append("g");
root.append("path")
.attr("class", "textPath")
.attr('id', d => `arc${d.bar}`)
.attr('d', labelArc)
.attr("stroke", "transparent")
.attr("fill", "none"),
root.append("text")
.attr("dy", 6)
.append("textPath")
.attr("xlink:href", d => `#arc${d.bar}`)
.style("fill", "red")
.style("text-anchor", "middle")
.attr("startOffset", "15%")
.attr("class", "labelText")
.attr("font-size", ".5em")
.attr("font-weight", "bold")
.attr("font-family", fontFamily)
.text(d => d.bar)
},
update => {
update.call(e => {
e.select("path").transition(t)
.attrTween("d", d => {
let oldEndAngle = d.labelEndAngle;
let oldStartAngle = d.labelStartAngle;
let i = d3.interpolate(oldStartAngle, (x(d.bar)) );
let u = d3.interpolate(oldEndAngle,(x(d.bar) + x.bandwidth()));
return t => {
d.labelEndAngle = u(t);
d.labelStartAngle = i(t);
return labelArc(d)};
}
)
}
)
},
exit => exit.remove()
)//end join
let bars = container
.selectAll(".barPath")
.data(data, d=>d.bar);
bars.join(
enter =>
enter.append("path")
.attr("class", "barPath")
.attr("fill", "#69b3a2")
.attr("opacity", 0.3)
.attr("d", arc)
,
update => update
.call(e => e.transition(t)
.attrTween("d", d => {
let oldEndAngle = d.endAngle;
let oldStartAngle = d.startAngle;
let i = d3.interpolate(oldStartAngle, x(d.bar));
let u = d3.interpolate(oldEndAngle,
x(d.bar) + x.bandwidth());
return t => {
d.endAngle = u(t);
d.startAngle = i(t);
return arc(d)
};
}
)
),
exit => exit.remove()
)
let circles = circlesContainer
.selectAll("circle")
.data(concentricData, d => d.id);
circles
.join(
enter => enter.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", d => d.radius)
.attr("stroke", "red")
.attr("stroke-width", 2)
.attr("fill", "none"),
update => update.call(e => e.transition(t)
.attr("stroke-width", (d,i) => sineScaleLineWidth(Math.sin(count*d.id) *concentricScale.bandwidth()))//randomInteger(2,concentricScale.bandwidth(), d.id))
.style("opacity", (d,i) => sineScale(Math.sin(count * d.id)))//randomInteger(2,9, d.id)/10)
)
)
}

update(data,concentricCircleData);
yield svg.node();
let count = 0;
d3.interval(() => {
//alternate between sorting and shuffling the data
count % 2 == 0 ? d3.shuffle(data) : data.sort((a,b) => a.bar - b.bar)

//update the order of the data in the scale for correct bar + label placement
let newDomain = data.map(d => d.bar);
x.domain(newDomain);
update(data,concentricCircleData,count);
count ++
}, 1500);
}
Insert cell
x = d3.scaleBand()
.domain(d3.range(numNodes))
.range([0, 2 * Math.PI])
Insert cell
y = d3.scaleRadial()
.domain([0, 100])
.range([innerRadius, outerRadius])
Insert cell
getXAxisTextRotation = (d) => {
return (x(d.bar) + x.bandwidth() / 2 + Math.PI) % (2 * Math.PI) < Math.PI ? "rotate(180)" : "rotate(0)";
}
Insert cell
arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(d => y(d['count']))
.startAngle(d => d.startAngle)
.endAngle(d => d.endAngle)
.padAngle(0.01)
.padRadius(innerRadius)
Insert cell
labelArc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(innerRadius + 10)
.startAngle(d => d.labelStartAngle)
.endAngle(d => d.labelEndAngle)
.padAngle(0.01)
Insert cell
data = d3.range(numNodes).map(function(i) {
return {
bar: i,
count: i,//Math.floor(Math.random() * (100 - 5) + 5),
startAngle: x(i),
endAngle: x(i) + x.bandwidth(),
labelStartAngle: x(i),
labelEndAngle: x(i) + x.bandwidth()
};
});
Insert cell
width = 400
Insert cell
height = 400
Insert cell
numNodes = 50
Insert cell
numCircles = 15
Insert cell
innerRadius = 140
Insert cell
outerRadius = Math.min(width, height) / 2;
Insert cell
labelRadius = 10
Insert cell
sineScaleLineWidth = d3.scaleLinear()
.range([1,concentricScale.bandwidth()/2,1])
.domain([1,-1])
Insert cell
sineScale = d3.scaleLinear()
.domain([1,-1])
.range([.1,.9,.1]);
Insert cell
concentricScale = d3.scaleBand()
.domain(d3.range(numCircles))
.range([10,innerRadius-5]);
Insert cell
concentricCircleData = d3.range(numCircles).map(d => {
return {
id: d,
radius: concentricScale(d)
};
})
Insert cell
randomInteger = (min,max, index) => {
return Math.floor(Math.random() * (max - min) + min);
}
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