Public
Edited
Oct 26, 2023
Insert cell
Insert cell
function gradientStackedBars(dataArray) {

const width = 800;
const height = 500;
const barWidth = 50;
const barSpacing = 100;

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);

// Calculate maximum total value from dataArray
const maxTotal = d3.max(dataArray, data => d3.sum(Object.values(data).filter(val => typeof val === 'number')));

// Define the y-scale
const yScale = d3.scaleLinear()
.domain([0, maxTotal])
.range([height, 0]); // reversed so that 0 is at the bottom of the SVG

// Define the x-scale
const xScale = d3.scaleBand()
.domain(dataArray.map((_, i) => i))
.range([0, width])
.padding(0.1);

dataArray.forEach((data, index) => {
if (index %2 !== 0){// Define gradient
const gradientId = `gradient${index}`;
const gradient = svg.append("defs")
.append("linearGradient")
.attr("id", gradientId)
.attr("x1", "0%")
.attr("y1", "100%")
.attr("x2", "0%")
.attr("y2", "0%");

let total = d3.sum(Object.values(data).filter(val => typeof val === 'number'));
let currentOffset = 0;

for (const [color, value] of Object.entries(data)) {
if (color !== 'date' && (value / total * 100) > 0) {
gradient.append("stop")
.attr("offset", `${currentOffset}%`)
.attr("stop-color", color);

currentOffset += (value / total * 100);

gradient.append("stop")
.attr("offset", `${currentOffset - 25}%`)
.attr("stop-color", color);
}
}

// Create bar with gradient fill
svg.append("rect")
.attr("x", xScale(index))
.attr("y", yScale(total)) // The y attribute will be set based on the total for that bar
.attr("width", xScale.bandwidth())
.attr("height", height - yScale(total)) // Height of the bar is determined by the difference from the SVG height
.style("fill", `url(#${gradientId})`);} else {// Define gradient
const gradientId = `gradient${index}`;
const gradient = svg.append("defs")
.append("linearGradient")
.attr("id", gradientId)
.attr("x1", "0%")
.attr("y1", "100%")
.attr("x2", "0%")
.attr("y2", "0%");

let total = d3.sum(Object.values(data).filter(val => typeof val === 'number'));
let currentOffset = 0;

for (const [color, value] of Object.entries(data)) {
if (color !== 'date' && (value / total * 100) > 0) {
gradient.append("stop")
.attr("offset", `${currentOffset}%`)
.attr("stop-color", color);

currentOffset += (value / total * 100);

gradient.append("stop")
.attr("offset", `${currentOffset}%`)
.attr("stop-color", color);
}
}

// Create bar with gradient fill
svg.append("rect")
.attr("x", xScale(index))
.attr("y", yScale(total)) // The y attribute will be set based on the total for that bar
.attr("width", xScale.bandwidth())
.attr("height", height - yScale(total)) // Height of the bar is determined by the difference from the SVG height
.style("fill", `url(#${gradientId})`);}
});

return svg.node();
}
Insert cell
data = [{
'#38bdf8': 12,
'#4338ca': 19,
'#f43f5e': 12,
date: '2022-01-01'
}, {
'#38bdf8': 12,
'#4338ca': 19,
'#f43f5e': 12,
date: '2022-01-01'
},{
'#38bdf8': 12,
'#4338ca': 67,
'#f43f5e': 12,
date: '2022-02-01'
}, {
'#38bdf8': 12,
'#4338ca': 67,
'#f43f5e': 12,
date: '2022-02-01'
},{
'#38bdf8': 67,
'#4338ca': 48,
'#f43f5e': 0,
date: '2022-03-01'
},{
'#38bdf8': 67,
'#4338ca': 48,
'#f43f5e': 0,
date: '2022-03-01'
}]
Insert cell
gradientStackedBars(data)
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