/// 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)}
$${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));