feat: horizontal

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2024-11-21 21:52:20 +01:00
parent 97c0adaa3b
commit bbf5770828
Signed by: kjuulh
GPG Key ID: D85D7535F18F35FA

View File

@ -38,15 +38,22 @@
</style>
</head>
<body>
<h2>Space-Themed Continuous Heatmap</h2>
<h2>Live Activity</h2>
<div class="divider"></div>
<div id="chart"></div>
<div id="legend" class="legend"></div>
<script>
const horizontal = true;
const categoryAmount = 5;
// Dimensions
const margin = { top: 20, right: 20, bottom: 20, left: 50 };
const width = 800 - margin.left - margin.right;
const height = 500 - margin.top - margin.bottom;
const height = 85 * categoryAmount / 2 - margin.top - margin.bottom;
// Categories with labels
const categories = [
@ -60,7 +67,7 @@
{ id: 7, label: "Refund Processed", color: "#A9A9A9" },
{ id: 8, label: "Subscription Renewed", color: "#BDB76B" },
{ id: 9, label: "Feedback Submitted", color: "#00FFFF" },
];
].filter((c) => c.id < categoryAmount);
// Store last intensity for each category
const lastIntensity = new Map(categories.map(c => [c.id, Math.random()]));
@ -84,13 +91,26 @@
.attr("transform", `translate(${margin.left},${margin.top})`);
// Time scales
const y = d3.scaleTime()
.domain([new Date(Date.now() - 60000), new Date()]) // Past 1 min to now
.range([height, 0]);
var x, y;
if (horizontal) {
x = d3.scaleTime()
.domain([new Date(Date.now() - 60000), new Date()]) // Past 1 min to now
.range([width, 0]);
const x = d3.scaleBand()
.domain(categories.map(c => c.id)) // Category IDs
.range([0, width]);
y = d3.scaleBand()
.domain(categories.map(c => c.id)) // Category IDs
.range([0, height]);
} else {
y = d3.scaleTime()
.domain([new Date(Date.now() - 60000), new Date()]) // Past 1 min to now
.range([height, 0]);
x = d3.scaleBand()
.domain(categories.map(c => c.id)) // Category IDs
.range([0, width]);
}
// Heatmap group
const grid = svg.append("g");
@ -99,23 +119,43 @@
function updateHeatmap(data) {
const cells = grid.selectAll("rect").data(data, d => d.id);
// Enter new data
cells.enter()
.append("rect")
.attr("x", d => x(d.category))
.attr("width", x.bandwidth())
.attr("y", d => y(new Date(d.timestamp)))
.attr("height", 4) // Minimized spacing between blocks
.attr("fill", d => categories[d.category].color)
.attr("opacity", d => d.intensity)
.merge(cells) // Merge updates
.attr("y", d => y(new Date(d.timestamp)))
.attr("opacity", d => d.intensity)
.attr("visibility", d =>
y(new Date(d.timestamp)) >= 0 && y(new Date(d.timestamp)) <= height
? "visible"
: "hidden"
);
if (horizontal) {
cells.enter()
.append("rect")
.attr("y", d => y(d.category))
.attr("width", 12)
.attr("x", d => x(new Date(d.timestamp)))
.attr("height", 25) // Minimized spacing between blocks
.attr("fill", d => categories[d.category].color)
.attr("opacity", d => d.intensity)
.merge(cells) // Merge updates
.attr("x", d => x(new Date(d.timestamp)))
.attr("opacity", d => d.intensity)
.attr("visibility", d =>
x(new Date(d.timestamp)) >= 0 && x(new Date(d.timestamp)) <= width
? "visible"
: "hidden"
);
} else {
// Enter new data
cells.enter()
.append("rect")
.attr("x", d => x(d.category))
.attr("width", x.bandwidth())
.attr("y", d => y(new Date(d.timestamp)))
.attr("height", 4) // Minimized spacing between blocks
.attr("fill", d => categories[d.category].color)
.attr("opacity", d => d.intensity)
.merge(cells) // Merge updates
.attr("y", d => y(new Date(d.timestamp)))
.attr("opacity", d => d.intensity)
.attr("visibility", d =>
y(new Date(d.timestamp)) >= 0 && y(new Date(d.timestamp)) <= height
? "visible"
: "hidden"
);
}
// Remove old data that moves off the screen
cells.exit().remove();
@ -162,7 +202,11 @@
// Update the domain of the y-axis
const currentTime = new Date();
const pastTime = new Date(currentTime.getTime() - 60000);
y.domain([pastTime, currentTime]);
if (horizontal) {
x.domain([pastTime, currentTime]);
} else {
y.domain([pastTime, currentTime]);
}
// Update positions of all heatmap cells
updateHeatmap(allData);