feat: nearly there just need some better colors and make the graph more continuous
Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
parent
d5d1a30a4c
commit
e424fa2d03
88
index.html
88
index.html
@ -3,21 +3,35 @@
|
|||||||
<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 Continuous Scrolling</title>
|
<title>Live Heatmap with Intensity</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 {
|
||||||
display: block;
|
display: block;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
.y-axis {
|
|
||||||
display: none;
|
.legend {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-color {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h2>Live Heatmap with Continuous Scrolling</h2>
|
<h2>Live Heatmap with Intensity</h2>
|
||||||
<div id="chart"></div>
|
<div id="chart"></div>
|
||||||
|
<div id="legend" class="legend"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Dimensions
|
// Dimensions
|
||||||
@ -25,6 +39,33 @@
|
|||||||
const width = 800 - margin.left - margin.right;
|
const width = 800 - margin.left - margin.right;
|
||||||
const height = 500 - margin.top - margin.bottom;
|
const height = 500 - margin.top - margin.bottom;
|
||||||
|
|
||||||
|
// Categories with labels
|
||||||
|
const categories = [
|
||||||
|
{ id: 0, label: "User Onboarded", color: "#1f77b4" },
|
||||||
|
{ id: 1, label: "Payment Accepted", color: "#ff7f0e" },
|
||||||
|
{ id: 2, label: "Payment Failed", color: "#d62728" },
|
||||||
|
{ id: 3, label: "Product Added to Cart", color: "#9467bd" },
|
||||||
|
{ id: 4, label: "Checkout Started", color: "#2ca02c" },
|
||||||
|
{ id: 5, label: "Order Placed", color: "#8c564b" },
|
||||||
|
{ id: 6, label: "Order Cancelled", color: "#e377c2" },
|
||||||
|
{ id: 7, label: "Refund Processed", color: "#7f7f7f" },
|
||||||
|
{ id: 8, label: "Subscription Renewed", color: "#bcbd22" },
|
||||||
|
{ id: 9, label: "Feedback Submitted", color: "#17becf" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Store last intensity for each category
|
||||||
|
const lastIntensity = new Map(categories.map(c => [c.id, Math.random()]));
|
||||||
|
|
||||||
|
// Create Legend
|
||||||
|
const legend = d3.select("#legend");
|
||||||
|
categories.forEach(category => {
|
||||||
|
const item = legend.append("div").attr("class", "legend-item");
|
||||||
|
item.append("div")
|
||||||
|
.attr("class", "legend-color")
|
||||||
|
.style("background-color", category.color);
|
||||||
|
item.append("span").text(category.label);
|
||||||
|
});
|
||||||
|
|
||||||
// Create SVG
|
// Create SVG
|
||||||
const svg = d3.select("#chart")
|
const svg = d3.select("#chart")
|
||||||
.append("svg")
|
.append("svg")
|
||||||
@ -39,18 +80,10 @@
|
|||||||
.range([height, 0]);
|
.range([height, 0]);
|
||||||
|
|
||||||
const x = d3.scaleBand()
|
const x = d3.scaleBand()
|
||||||
.domain(d3.range(10)) // Example: 10 categories
|
.domain(categories.map(c => c.id)) // Category IDs
|
||||||
.range([0, width]);
|
.range([0, width]);
|
||||||
|
|
||||||
// Heatmap color scale
|
// Heatmap group
|
||||||
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(6));
|
|
||||||
|
|
||||||
const grid = svg.append("g");
|
const grid = svg.append("g");
|
||||||
|
|
||||||
// Initialize heatmap
|
// Initialize heatmap
|
||||||
@ -64,9 +97,11 @@
|
|||||||
.attr("width", x.bandwidth())
|
.attr("width", x.bandwidth())
|
||||||
.attr("y", d => y(new Date(d.timestamp)))
|
.attr("y", d => y(new Date(d.timestamp)))
|
||||||
.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 => categories[d.category].color)
|
||||||
|
.attr("opacity", d => d.intensity)
|
||||||
.merge(cells) // Merge updates
|
.merge(cells) // Merge updates
|
||||||
.attr("y", d => y(new Date(d.timestamp)))
|
.attr("y", d => y(new Date(d.timestamp)))
|
||||||
|
.attr("opacity", d => d.intensity)
|
||||||
.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"
|
||||||
@ -80,15 +115,21 @@
|
|||||||
// Real-time simulation
|
// Real-time simulation
|
||||||
let allData = [];
|
let allData = [];
|
||||||
const scrollingSpeed = 10; // Pixels per second
|
const scrollingSpeed = 10; // Pixels per second
|
||||||
const pixelsPerMillisecond = scrollingSpeed / 1000;
|
|
||||||
|
|
||||||
function generateData() {
|
function generateData() {
|
||||||
const newData = Array.from({ length: 10 }, (_, i) => ({
|
// Simulate sporadic events with intensity
|
||||||
id: `${Date.now()}-${i}`,
|
const newData = categories.map(c => {
|
||||||
category: i,
|
if (Math.random() < 0.5) return null; // 50% chance no data for this category
|
||||||
timestamp: Date.now(),
|
const newIntensity = Math.random(); // Random intensity
|
||||||
value: Math.random() * 100,
|
const smoothIntensity = d3.interpolate(lastIntensity.get(c.id), newIntensity)(0.5); // Smooth transition
|
||||||
}));
|
lastIntensity.set(c.id, smoothIntensity); // Update last intensity
|
||||||
|
return {
|
||||||
|
id: `${Date.now()}-${c.id}`,
|
||||||
|
category: c.id,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
intensity: smoothIntensity,
|
||||||
|
};
|
||||||
|
}).filter(Boolean); // Remove null values
|
||||||
|
|
||||||
// Append new data and remove older ones beyond the last 60 seconds
|
// Append new data and remove older ones beyond the last 60 seconds
|
||||||
allData = [...allData, ...newData].filter(d =>
|
allData = [...allData, ...newData].filter(d =>
|
||||||
@ -116,9 +157,6 @@
|
|||||||
|
|
||||||
// Update positions of all heatmap cells
|
// Update positions of all heatmap cells
|
||||||
updateHeatmap(allData);
|
updateHeatmap(allData);
|
||||||
|
|
||||||
// Smoothly shift axis
|
|
||||||
yAxis.call(d3.axisLeft(y).ticks(6));
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
Loading…
Reference in New Issue
Block a user