viewof kickstarterRacingChart = {
const container = html`
<div style="font-family: Arial, sans-serif; max-width: 1000px; text-align: center;">
<style>
h1 { text-align: center; font-size: 20px; margin-bottom: 10px; }
.control-container { display: flex; justify-content: center; gap: 10px; margin-bottom: 15px; }
button { padding: 8px 15px; font-size: 14px; border: none; cursor: pointer; border-radius: 5px; transition: 0.3s; }
.play { background-color: green; color: white; }
.pause { background-color: red; color: white; }
button:hover { opacity: 0.8; }
svg { width: 900px; height: 550px; display: block; margin: auto; background: white; border-radius: 10px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2); }
.year-label { font-size: 80px; font-weight: bold; fill: gray; text-anchor: middle; opacity: 0.7; }
.axis-label { font-size: 16px; font-weight: bold; }
</style>
<h1>📊 Kickstarter Funding by Category (Racing Bar Chart)</h1>
<div class="control-container">
<button class="play">▶ Play</button>
<button class="pause">⏸ Pause</button>
</div>
<svg></svg>
</div>`;
let isPlaying = false;
let timer;
let speed = 4000;
let yearIndex = 0;
let years = [];
let fundingData;
const svg = d3.select(container).select("svg"),
width = 900,
height = 550,
margin = { top: 40, right: 100, bottom: 80, left: 220 };
const x = d3.scaleLinear().range([margin.left, width - margin.right]);
const y = d3.scaleBand().range([margin.top, height - margin.bottom]).padding(0.4);
const color = d3.scaleOrdinal(d3.schemeCategory10);
const gMain = svg.append("g");
const xAxis = gMain.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.attr("class", "x-axis");
const yAxis = gMain.append("g")
.attr("transform", `translate(${margin.left},0)`)
.attr("class", "y-axis");
gMain.append("text")
.attr("class", "axis-label")
.attr("x", width / 2)
.attr("y", height - 40)
.attr("text-anchor", "middle")
.text("💰 Pledged Amount (USD)");
gMain.append("text")
.attr("class", "axis-label")
.attr("transform", "rotate(-90)")
.attr("x", -height / 2)
.attr("y", margin.left - 190)
.attr("text-anchor", "middle")
.text("📂 Category");
const yearLabel = gMain.append("text")
.attr("class", "year-label")
.attr("x", width / 2)
.attr("y", height / 2)
.attr("text-anchor", "middle")
.text("");
async function initialize() {
const data = await FileAttachment("Kickstarter_racing_chart.tsv").text();
const parsedData = d3.tsvParse(data, d => {
d.YEAR = +d.YEAR; // Ensure it's a number
d.PLEDGED_IN_USD = +d.PLEDGED_IN_USD; // Convert to number
return d;
});
// Group funding by Year and Category
fundingData = d3.rollup(parsedData,
v => d3.sum(v, d => d.PLEDGED_IN_USD),
d => d.YEAR,
d => d.CATEGORY
);
years = Array.from(fundingData.keys()).sort(d3.ascending);
console.log("Years:", years); // Debugging Step
updateChart();
}
function updateChart() {
if (!isPlaying || yearIndex >= years.length) return;
let currentYear = years[yearIndex];
console.log("Current Year:", currentYear); // Debugging Step
let categories = Array.from(fundingData.get(currentYear) || []);
categories.sort((a, b) => b[1] - a[1]);
let formattedData = categories.map(d => ({
category: d[0],
pledged: d[1]
}));
x.domain([0, d3.max(formattedData, d => d.pledged)]);
y.domain(formattedData.map(d => d.category));
gMain.select(".x-axis")
.transition().duration(speed)
.call(d3.axisBottom(x).ticks(5).tickFormat(d => `$${d3.format(",")(d)}`));
gMain.select(".y-axis")
.transition().duration(speed)
.call(d3.axisLeft(y));
let bars = gMain.selectAll("rect").data(formattedData, d => d.category);
bars.enter()
.append("rect")
.attr("x", margin.left)
.attr("y", d => y(d.category))
.attr("height", y.bandwidth())
.attr("fill", d => color(d.category))
.merge(bars)
.transition().duration(speed)
.attr("width", d => x(d.pledged) - margin.left)
.attr("y", d => y(d.category));
bars.exit().transition().duration(500).attr("width", 0).remove();
let labels = gMain.selectAll(".label").data(formattedData, d => d.category);
labels.enter()
.append("text")
.attr("class", "label")
.attr("x", d => x(d.pledged) + 5)
.attr("y", d => y(d.category) + y.bandwidth() / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "start")
.attr("fill", "black")
.attr("font-weight", "bold")
.merge(labels)
.transition().duration(speed)
.tween("text", function(d) {
let i = d3.interpolateNumber(0, d.pledged);
return function(t) { this.textContent = `$${d3.format(",")(Math.round(i(t)))}`; };
})
.attr("x", d => x(d.pledged) + 5)
.attr("y", d => y(d.category) + y.bandwidth() / 2);
labels.exit().transition().duration(500).attr("opacity", 0).remove();
yearLabel.transition().duration(speed).text(currentYear);
yearIndex++;
timer = setTimeout(updateChart, speed + 1000);
}
function playAnimation() {
if (!isPlaying) {
isPlaying = true;
updateChart();
}
}
function pauseAnimation() {
isPlaying = false;
clearTimeout(timer);
}
container.querySelector(".play").addEventListener("click", playAnimation);
container.querySelector(".pause").addEventListener("click", pauseAnimation);
initialize();
return container;
}