feat: fully continuous scroll

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2024-11-21 21:11:23 +01:00
parent 23885cd9e4
commit f31c9a7022
Signed by: kjuulh
GPG Key ID: D85D7535F18F35FA

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Live Heatmap with Smooth Scrolling</title> <title>Live Heatmap with Continuous Scrolling</title>
<script src="https://d3js.org/d3.v7.min.js"></script> <script src="https://d3js.org/d3.v7.min.js"></script>
<style> <style>
svg { svg {
@ -13,7 +13,7 @@
</style> </style>
</head> </head>
<body> <body>
<h2>Live Heatmap with Smooth Scrolling</h2> <h2>Live Heatmap with Continuous Scrolling</h2>
<div id="chart"></div> <div id="chart"></div>
<script> <script>
@ -46,7 +46,7 @@
// Axis // Axis
const yAxis = svg.append("g") const yAxis = svg.append("g")
.attr("class", "y-axis") .attr("class", "y-axis")
.call(d3.axisLeft(y).ticks(20)); .call(d3.axisLeft(y).ticks(6));
const grid = svg.append("g"); const grid = svg.append("g");
@ -63,29 +63,23 @@
.attr("height", 5) // Fixed height for heatmap blocks .attr("height", 5) // Fixed height for heatmap blocks
.attr("fill", d => colorScale(d.value)) .attr("fill", d => colorScale(d.value))
.merge(cells) // Merge updates .merge(cells) // Merge updates
.transition()
.duration(500)
.attr("y", d => y(new Date(d.timestamp))) .attr("y", d => y(new Date(d.timestamp)))
.attr("visibility", d => .attr("visibility", d =>
y(new Date(d.timestamp)) >= 0 && y(new Date(d.timestamp)) <= height y(new Date(d.timestamp)) >= 0 && y(new Date(d.timestamp)) <= height
? "visible" ? "visible"
: "hidden" : "hidden"
); // Hide items out of bounds );
// Remove old data that moves off the screen // Remove old data that moves off the screen
cells.exit().remove(); cells.exit().remove();
// Shift all cells upward to simulate scrolling
grid.selectAll("rect")
.transition()
.duration(500)
.attr("y", d => y(new Date(d.timestamp)));
} }
// Simulate real-time updates // Real-time simulation
let allData = []; let allData = [];
setInterval(() => { const scrollingSpeed = 10; // Pixels per second
// Generate random data const pixelsPerMillisecond = scrollingSpeed / 1000;
function generateData() {
const newData = Array.from({ length: 10 }, (_, i) => ({ const newData = Array.from({ length: 10 }, (_, i) => ({
id: `${Date.now()}-${i}`, id: `${Date.now()}-${i}`,
category: i, category: i,
@ -93,18 +87,29 @@
value: Math.random() * 100, value: Math.random() * 100,
})); }));
// Add new data to the global array and keep only recent data
allData = [...allData, ...newData].filter(d => allData = [...allData, ...newData].filter(d =>
new Date(d.timestamp) >= new Date(Date.now() - 60000) new Date(d.timestamp) >= new Date(Date.now() - 60000)
); );
}
// Update time scale domain // Continuous scroll
y.domain([new Date(Date.now() - 60000), new Date()]); d3.timer(elapsed => {
yAxis.transition().duration(500).call(d3.axisLeft(y).ticks(20)); // Generate data periodically (every 1 second)
if (Math.floor(elapsed) % 1000 === 0) {
generateData();
}
// Update heatmap with filtered data // Update the domain of the y-axis
const now = new Date();
const past = new Date(now.getTime() - 60000);
y.domain([past, now]);
// Update positions of all heatmap cells
updateHeatmap(allData); updateHeatmap(allData);
}, 1000);
// Smoothly shift axis
yAxis.call(d3.axisLeft(y).ticks(6));
});
</script> </script>
</body> </body>
</html> </html>