feat: with no fade continuous
Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
parent
fa3cc87b9b
commit
23885cd9e4
153
index.html
153
index.html
@ -3,109 +3,108 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Live Timeline Heatmap</title>
|
||||
<title>Live Heatmap with Smooth Scrolling</title>
|
||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||
<style>
|
||||
.heatmap rect {
|
||||
//stroke: #aaa;
|
||||
svg {
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Live Timeline Heatmap</h2>
|
||||
<h2>Live Heatmap with Smooth Scrolling</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
|
||||
// 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;
|
||||
|
||||
// Create SVG
|
||||
const svg = d3.select("#chart")
|
||||
.append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("class", "heatmap");
|
||||
.attr("transform", `translate(${margin.left},${margin.top})`);
|
||||
|
||||
// Initialize grid
|
||||
let grid = Array.from({ length: numRows }, () =>
|
||||
Array.from({ length: numCols }, () => 0)
|
||||
);
|
||||
// Time scales
|
||||
const y = d3.scaleTime()
|
||||
.domain([new Date(Date.now() - 60000), new Date()]) // Past 1 min to now
|
||||
.range([height, 0]);
|
||||
|
||||
// Color scale
|
||||
const colorScale = d3.scaleSequential(d3.interpolateTurbo).domain([0, maxIntensity]);
|
||||
const x = d3.scaleBand()
|
||||
.domain(d3.range(10)) // Example: 10 categories
|
||||
.range([0, width]);
|
||||
|
||||
// Create grid rectangles
|
||||
svg.selectAll("rect")
|
||||
.data(grid.flat())
|
||||
.enter()
|
||||
// Heatmap color scale
|
||||
const colorScale = d3.scaleSequential(d3.interpolateYlOrRd)
|
||||
.domain([0, 100]); // Adjust for event intensity
|
||||
|
||||
// Axis
|
||||
const yAxis = svg.append("g")
|
||||
.attr("class", "y-axis")
|
||||
.call(d3.axisLeft(y).ticks(20));
|
||||
|
||||
const grid = svg.append("g");
|
||||
|
||||
// Initialize heatmap
|
||||
function updateHeatmap(data) {
|
||||
const cells = grid.selectAll("rect").data(data, d => d.id);
|
||||
|
||||
// Enter new data
|
||||
cells.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())
|
||||
.attr("x", d => x(d.category))
|
||||
.attr("width", x.bandwidth())
|
||||
.attr("y", d => y(new Date(d.timestamp)))
|
||||
.attr("height", 5) // Fixed height for heatmap blocks
|
||||
.attr("fill", d => colorScale(d.value))
|
||||
.merge(cells) // Merge updates
|
||||
.transition()
|
||||
.duration(200)
|
||||
.attr("fill", d => colorScale(d));
|
||||
}
|
||||
.duration(500)
|
||||
.attr("y", d => y(new Date(d.timestamp)))
|
||||
.attr("visibility", d =>
|
||||
y(new Date(d.timestamp)) >= 0 && y(new Date(d.timestamp)) <= height
|
||||
? "visible"
|
||||
: "hidden"
|
||||
); // Hide items out of bounds
|
||||
|
||||
// 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
|
||||
// Remove old data that moves off the screen
|
||||
cells.exit().remove();
|
||||
|
||||
// Update grid positions
|
||||
svg.selectAll("rect")
|
||||
.data(grid.flat())
|
||||
// Shift all cells upward to simulate scrolling
|
||||
grid.selectAll("rect")
|
||||
.transition()
|
||||
.duration(200)
|
||||
.attr("y", (_, i) => Math.floor(i / numCols) * cellHeight);
|
||||
.duration(500)
|
||||
.attr("y", d => y(new Date(d.timestamp)));
|
||||
}
|
||||
|
||||
// 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
|
||||
// Simulate real-time updates
|
||||
let allData = [];
|
||||
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
|
||||
// Generate random data
|
||||
const newData = Array.from({ length: 10 }, (_, i) => ({
|
||||
id: `${Date.now()}-${i}`,
|
||||
category: i,
|
||||
timestamp: Date.now(),
|
||||
value: Math.random() * 100,
|
||||
}));
|
||||
|
||||
// Animation loop
|
||||
d3.interval(() => {
|
||||
scrollHeatmap();
|
||||
updateHeatmap();
|
||||
}, 500); // Updates every 500ms
|
||||
// Add new data to the global array and keep only recent data
|
||||
allData = [...allData, ...newData].filter(d =>
|
||||
new Date(d.timestamp) >= new Date(Date.now() - 60000)
|
||||
);
|
||||
|
||||
// Update time scale domain
|
||||
y.domain([new Date(Date.now() - 60000), new Date()]);
|
||||
yAxis.transition().duration(500).call(d3.axisLeft(y).ticks(20));
|
||||
|
||||
// Update heatmap with filtered data
|
||||
updateHeatmap(allData);
|
||||
}, 1000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user