EoQ_Supporting_Files/indexbar2.html
2025-05-07 21:24:06 +01:00

163 lines
3.8 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Bar Chart - Fixed Height Bars</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
body {
font-family: sans-serif;
margin: 40px;
}
.bar {
fill: steelblue;
stroke: black;
stroke-width: 1px;
}
.axis path,
.axis line {
stroke: black;
shape-rendering: crispEdges;
}
#tooltip {
position: absolute;
visibility: hidden;
background-color: white;
border: 1px solid black;
padding: 4px;
font-size: 12px;
pointer-events: none;
}
</style>
</head>
<body>
<svg width="600" height="250"></svg>
<script>
const svg = d3.select("svg");
const width = +svg.attr("width");
const height = +svg.attr("height");
const margin = { top: 40, right: 30, bottom: 100, left: 30 };
const data = {
"-3": 0,
"-2": 1,
"-1": 3,
"0": 0,
"1": 2,
"2": 0,
"3": 6
};
const categories = ["Extremely Bad", "Very Bad", "Bad", "Neutral", "Good", "Very Good", "Extremely Good"];
// Convert object to array of {key, value}
const dataArray = Object.entries(data).map(([key, count]) => ({
key: +key,
count: count,
show: count > 0
}));
// Scales
const x = d3.scaleBand()
.domain(categories) // [-3, -2, ..., 3]
.range([margin.left, width - margin.right])
.padding(0); //space between bars
const y = d3.scaleLinear()
.domain([0, 1]) // fixed height of 1
.range([height - margin.bottom, margin.top]);
// X-axis
const tickValues = d3.range(-3.5, 3.5, 0.5);
svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).tickFormat(d => d))
.selectAll("text")
.attr("transform", "rotate(-45)") // Rotate them
.attr("x", -10) // Adjust x position for rotation
.attr("y", 5) // Adjust y position for rotation
.style("text-anchor", "end") // Anchor text to the start for rotation
.style("font-size", "12px"); // Adjust font size as needed
// Draw bars
svg.selectAll(".bar")
.data(dataArray)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", d => x(categories[d.key+3]))
.attr("y", d => d.show ? y(1) : y(0))
.attr("width", x.bandwidth())
.attr("height", d => d.show ? y(0) - y(1) : 0);
//Add values above bars:
svg.selectAll(".bar-label")
.data(dataArray)
.enter()
.append("text")
.attr("class", "bar-label")
.attr("x", d => x(categories[d.key+3]) + x.bandwidth() / 2)
.attr("y", d => d.count > 0 ? y(1) - 5 : y(0)) // 5px above the bar
.attr("text-anchor", "middle")
.attr("font-size", "12px")
.attr("fill", "black")
.text(d => d.count > 0 ? d.count : "");
// Display average line
const totalCount = d3.sum(dataArray, d => d.count);
const weightedSum = d3.sum(dataArray, d => d.key * d.count);
const average = totalCount > 0 ? weightedSum / totalCount : 0;
const xLinear = d3.scaleLinear()
.domain([-3, 3])
.range([margin.left + x.bandwidth() / 2, width - margin.right - x.bandwidth() / 2]);
const averageX = xLinear(average);
// Draw the vertical red line
svg.append("line")
.attr("x1", averageX)
.attr("x2", averageX)
.attr("y1", y(0))
.attr("y2", y(1) - 20) //raise by 20px
.attr("stroke", "red")
.attr("stroke-width", 2)
.attr("stroke-dasharray", "4,2"); // Optional dashed line
// Add "Average" label above the line
const formatAvg = d3.format(".2f");
svg.append("text")
.attr("x", averageX)
.attr("y", y(1) - 30) //was -10
.attr("text-anchor", "middle")
.attr("fill", "red")
.attr("font-size", "12px")
//.text(`Average(mean): ${formatAvg(average)}`);
.text(`Average(mean)`);
// Title beneath the graph
svg.append("text")
.attr("x", width / 2)
.attr("y", height - 5) // Just below the x-axis
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("fill", "black")
.text("Demo bar graph");
</script>
</body>
</html>