161 lines
4.7 KiB
JavaScript
161 lines
4.7 KiB
JavaScript
/// <reference path="./d3.js" />
|
|
|
|
const ASPECT_RATIO = window.innerWidth / window.innerHeight;
|
|
const CANVAS_WIDTH = window.innerWidth ?? 1000;
|
|
// *0.9 for footer
|
|
const CANVAS_HEIGHT =
|
|
(ASPECT_RATIO > 1
|
|
? window.innerHeight * 0.9
|
|
: CANVAS_WIDTH * ASPECT_RATIO * 0.9) ?? 500;
|
|
const CHART_WIDTH = CANVAS_WIDTH * 0.8;
|
|
const CHART_HEIGHT = CANVAS_HEIGHT * 0.8;
|
|
const CHART_X_OFFSET = (CANVAS_WIDTH - CHART_WIDTH) / 2;
|
|
const CHART_Y_OFFSET = (CANVAS_HEIGHT - CHART_HEIGHT) / 2;
|
|
|
|
const fetchData = async () => {
|
|
return await (await fetch("./data.json")).json();
|
|
};
|
|
|
|
const getQuarter = (month) => {
|
|
if (month >= 1 && month <= 3) return 1;
|
|
else if (month >= 4 && month <= 6) return 2;
|
|
else if (month >= 7 && month <= 9) return 3;
|
|
else if (month >= 10 && month <= 12) return 4;
|
|
else throw Error("Month out of range: " + month);
|
|
};
|
|
|
|
const parseDate = (date) => {
|
|
const [year, month] = date.split("-");
|
|
const quarter = getQuarter(Number(month)).toString();
|
|
return year + " Q" + quarter;
|
|
};
|
|
|
|
const parseYear = (date) => Number(date.split("-")[0]);
|
|
|
|
fetchData()
|
|
.then((dataset) => {
|
|
const MAX_GDP = Number(d3.max(dataset.data, (d) => d[1]));
|
|
const BAR_WIDTH = CHART_WIDTH / dataset.data.length;
|
|
const yScale = (gdp) => (gdp / MAX_GDP) * CHART_HEIGHT;
|
|
const xAxisScale = d3
|
|
.scaleLinear()
|
|
.domain([
|
|
parseYear(dataset.data[0][0]),
|
|
parseYear(dataset.data.at(-1)[0]),
|
|
])
|
|
.range([0, CHART_WIDTH]);
|
|
const yAxisScale = d3
|
|
.scaleLinear()
|
|
.domain([0, MAX_GDP])
|
|
.range([CHART_HEIGHT, 0]);
|
|
|
|
const canvas = d3
|
|
.select("body")
|
|
.append("svg")
|
|
.attr("width", CANVAS_WIDTH)
|
|
.attr("height", CANVAS_HEIGHT);
|
|
|
|
const mouseover = (e) => {
|
|
const { date, gdp } = e.target.dataset;
|
|
tooltip
|
|
.html(`${parseDate(date)}<br/>$${Number(gdp).toLocaleString()} Billion`)
|
|
.style("opacity", 1)
|
|
.attr("data-date", date)
|
|
.attr("data-gdp", gdp);
|
|
};
|
|
|
|
const mousemove = (e) => {
|
|
tooltip.style("left", e.pageX + "px").style("top", e.pageY + "px");
|
|
};
|
|
|
|
const mouseleave = (d) => {
|
|
tooltip.style("opacity", 0);
|
|
};
|
|
|
|
canvas
|
|
.append("text")
|
|
.attr("id", "title")
|
|
.attr("x", "50%")
|
|
.attr("text-anchor", "middle")
|
|
.attr("y", CHART_Y_OFFSET * 0.9)
|
|
.attr("font-size", CHART_Y_OFFSET * 0.8)
|
|
.text("United States GDP");
|
|
|
|
canvas
|
|
.selectAll("rect")
|
|
.data(dataset.data)
|
|
.enter()
|
|
.append("rect")
|
|
.attr("class", "bar")
|
|
.attr("data-date", (d) => d[0])
|
|
.attr("data-gdp", (d) => d[1])
|
|
.attr("height", (d) => yScale(d[1]))
|
|
.attr("width", BAR_WIDTH)
|
|
.attr("x", (_, i) => i * BAR_WIDTH + CHART_X_OFFSET)
|
|
.attr("y", (d) => CHART_Y_OFFSET + CHART_HEIGHT - yScale(d[1]))
|
|
.on("mouseover", mouseover)
|
|
.on("mousemove", mousemove)
|
|
.on("mouseleave", mouseleave);
|
|
|
|
canvas
|
|
.append("g")
|
|
.attr("id", "x-axis")
|
|
.style("font", CHART_Y_OFFSET * 0.3 + "px quicksand")
|
|
.attr(
|
|
"transform",
|
|
`translate(${CHART_X_OFFSET}, ${CHART_HEIGHT + CHART_Y_OFFSET})`
|
|
)
|
|
.call(d3.axisBottom(xAxisScale).tickFormat(d3.format("d")));
|
|
|
|
canvas
|
|
.append("g")
|
|
.attr("id", "y-axis")
|
|
.style("font", CHART_Y_OFFSET * 0.3 + "px quicksand")
|
|
.attr("transform", `translate(${CHART_X_OFFSET}, ${CHART_Y_OFFSET})`)
|
|
.call(d3.axisLeft(yAxisScale));
|
|
|
|
let tooltip = d3
|
|
.select("body")
|
|
.append("div")
|
|
.attr("id", "tooltip")
|
|
.attr("class", "tooltip")
|
|
.attr("data-date", "")
|
|
.attr("data-gdp", "")
|
|
.style("opacity", 0)
|
|
.style("position", "absolute")
|
|
.style("font", CHART_Y_OFFSET * 0.3 + "px");
|
|
|
|
canvas
|
|
.append("text")
|
|
.attr("id", "y-label")
|
|
.style("font-size", CHART_Y_OFFSET * 0.3 + "px")
|
|
.attr("text-anchor", "end")
|
|
.attr("y", CHART_X_OFFSET * 0.2)
|
|
.attr("x", -CHART_Y_OFFSET - CHART_HEIGHT / 2)
|
|
.attr("text-anchor", "middle")
|
|
.attr("transform", "rotate(-90)")
|
|
.text("Gross Domestic Product (GDP) in Billions");
|
|
|
|
canvas
|
|
.append("text")
|
|
.attr("id", "x-label")
|
|
.attr("x", "50%")
|
|
.attr("y", CHART_HEIGHT + CHART_Y_OFFSET * 1.9)
|
|
.attr("text-anchor", "middle")
|
|
.style("font-size", CHART_Y_OFFSET * 0.3 + "px")
|
|
.text("Quarterly");
|
|
|
|
d3.select("body")
|
|
.append("footer")
|
|
.style("bottom", CHART_Y_OFFSET * 0.1 + "px")
|
|
.style("font-size", CHART_Y_OFFSET * 0.25 + "px")
|
|
.append("p")
|
|
.append("a")
|
|
.attr(
|
|
"href",
|
|
"https://radii.dev/freecodecamp-data-visualization/bar-chart"
|
|
)
|
|
.text("</> Source Code & License");
|
|
})
|
|
.catch((e) => console.error("Error occurred!", e));
|