112 lines
3.5 KiB
HTML
112 lines
3.5 KiB
HTML
|
<!DOCTYPE html>
|
||
|
<html lang="en">
|
||
|
<head>
|
||
|
<meta charset="UTF-8">
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
<title>Live Timeline Heatmap</title>
|
||
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||
|
<style>
|
||
|
.heatmap rect {
|
||
|
//stroke: #aaa;
|
||
|
}
|
||
|
</style>
|
||
|
</head>
|
||
|
<body>
|
||
|
<h2>Live Timeline Heatmap</h2>
|
||
|
<div id="chart"></div>
|
||
|
|
||
|
<script>
|
||
|
// Configuration
|
||
|
const width = 500;
|
||
|
const height = 500;
|
||
|
const numCols = 10; // Number of columns
|
||
|
const numRows = 20; // Number of rows
|
||
|
const cellWidth = width / numCols; // Cell width
|
||
|
const cellHeight = height / numRows; // Cell height
|
||
|
const fadeOutRate = 0.8; // Intensity decay
|
||
|
const maxIntensity = 10; // Maximum intensity value
|
||
|
|
||
|
// Create SVG
|
||
|
const svg = d3.select("#chart")
|
||
|
.append("svg")
|
||
|
.attr("width", width)
|
||
|
.attr("height", height)
|
||
|
.append("g")
|
||
|
.attr("class", "heatmap");
|
||
|
|
||
|
// Initialize grid
|
||
|
let grid = Array.from({ length: numRows }, () =>
|
||
|
Array.from({ length: numCols }, () => 0)
|
||
|
);
|
||
|
|
||
|
// Color scale
|
||
|
const colorScale = d3.scaleSequential(d3.interpolateTurbo).domain([0, maxIntensity]);
|
||
|
|
||
|
// Create grid rectangles
|
||
|
svg.selectAll("rect")
|
||
|
.data(grid.flat())
|
||
|
.enter()
|
||
|
.append("rect")
|
||
|
.attr("x", (_, i) => (i % numCols) * cellWidth)
|
||
|
.attr("y", (_, i) => Math.floor(i / numCols) * cellHeight)
|
||
|
.attr("width", cellWidth)
|
||
|
.attr("height", cellHeight)
|
||
|
.attr("fill", colorScale(0));
|
||
|
|
||
|
// Function to update the heatmap
|
||
|
function updateHeatmap() {
|
||
|
// Decay old values
|
||
|
for (let row of grid) {
|
||
|
for (let col = 0; col < row.length; col++) {
|
||
|
row[col] *= fadeOutRate;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Update heatmap colors
|
||
|
svg.selectAll("rect")
|
||
|
.data(grid.flat())
|
||
|
.transition()
|
||
|
.duration(200)
|
||
|
.attr("fill", d => colorScale(d));
|
||
|
}
|
||
|
|
||
|
// Scroll rows upward and add a new empty row at the bottom
|
||
|
function scrollHeatmap() {
|
||
|
grid.shift(); // Remove the top row
|
||
|
grid.push(Array.from({ length: numCols }, () => 0)); // Add an empty row at the bottom
|
||
|
|
||
|
// Update grid positions
|
||
|
svg.selectAll("rect")
|
||
|
.data(grid.flat())
|
||
|
.transition()
|
||
|
.duration(200)
|
||
|
.attr("y", (_, i) => Math.floor(i / numCols) * cellHeight);
|
||
|
}
|
||
|
|
||
|
// Function to add events to the bottom row
|
||
|
function addEvent(col, intensity) {
|
||
|
if (col >= 0 && col < numCols) {
|
||
|
grid[grid.length - 1][col] = Math.min(
|
||
|
grid[grid.length - 1][col] + intensity,
|
||
|
maxIntensity
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Simulate incoming events
|
||
|
setInterval(() => {
|
||
|
const randomCol = Math.floor(Math.random() * numCols);
|
||
|
const randomIntensity = Math.random() * 8 + 2; // Random intensity (2-10)
|
||
|
addEvent(randomCol, randomIntensity);
|
||
|
}, 300); // Events every 300ms
|
||
|
|
||
|
// Animation loop
|
||
|
d3.interval(() => {
|
||
|
scrollHeatmap();
|
||
|
updateHeatmap();
|
||
|
}, 500); // Updates every 500ms
|
||
|
</script>
|
||
|
</body>
|
||
|
</html>
|
||
|
|