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