This example demonstrates how to create a dynamic and visually appealing heatmap using Scalable Vector Graphics (SVG). Heatmaps are excellent for visualizing the magnitude of a phenomenon as color in two dimensions. Hover over cells to see their specific values.
The heatmap is constructed using SVG elements (rectangles) where the fill color of each rectangle corresponds to a data value. A gradient color scale is used to represent the range of values. Interactive tooltips are implemented using JavaScript to display precise data on hover.
// Sample data (replace with your actual data)
const heatmapData = [
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
[15, 25, 35, 45, 55, 65, 75, 85, 95, 10],
[20, 30, 40, 50, 60, 70, 80, 90, 10, 20],
[25, 35, 45, 55, 65, 75, 85, 90, 20, 30],
[30, 40, 50, 60, 70, 80, 90, 10, 30, 40],
[35, 45, 55, 65, 75, 85, 90, 20, 40, 50],
[40, 50, 60, 70, 80, 90, 10, 30, 50, 60],
[45, 55, 65, 75, 85, 90, 20, 40, 60, 70],
[50, 60, 70, 80, 90, 10, 30, 50, 70, 80],
[55, 65, 75, 85, 90, 20, 40, 60, 80, 90]
];
const svg = document.getElementById('heatmapSvg');
const tooltip = document.getElementById('tooltip');
const svgWidth = svg.width.baseVal.value;
const svgHeight = svg.height.baseVal.value;
const numRows = heatmapData.length;
const numCols = heatmapData[0].length;
const cellWidth = svgWidth / numCols;
const cellHeight = svgHeight / numRows;
const minValue = Math.min(...heatmapData.flat());
const maxValue = Math.max(...heatmapData.flat());
// Define color stops for the gradient (you can customize this)
const colorStops = [
{ value: 0, color: '#f0fff0' }, // Honeydew (low)
{ value: 0.2, color: '#ffffb3' }, // Light Yellow
{ value: 0.4, color: '#ffe066' }, // Yellow
{ value: 0.6, color: '#ffcc00' }, // Gold
{ value: 0.8, color: '#ff9900' }, // Orange
{ value: 1, color: '#cc0000' } // Red (high)
];
// Function to get color based on value
function getColor(value) {
const normalizedValue = (value - minValue) / (maxValue - minValue);
for (let i = 0; i < colorStops.length - 1; i++) {
if (normalizedValue >= colorStops[i].value && normalizedValue <= colorStops[i + 1].value) {
const progress = (normalizedValue - colorStops[i].value) / (colorStops[i + 1].value - colorStops[i].value);
const startColor = hexToRgb(colorStops[i].color);
const endColor = hexToRgb(colorStops[i + 1].color);
const r = Math.round(startColor.r + (endColor.r - startColor.r) * progress);
const g = Math.round(startColor.g + (endColor.g - startColor.g) * progress);
const b = Math.round(startColor.b + (endColor.b - startColor.b) * progress);
return `rgb(${r}, ${g}, ${b})`;
}
}
return colorStops[colorStops.length - 1].color; // Default to max color if something goes wrong
}
// Helper function to convert hex color to RGB
function hexToRgb(hex) {
const bigint = parseInt(hex.slice(1), 16);
const r = (bigint >> 16) & 255;
const g = (bigint >> 8) & 255;
const b = bigint & 255;
return { r, g, b };
}
// Create SVG elements
for (let i = 0; i < numRows; i++) {
for (let j = 0; j < numCols; j++) {
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('x', j * cellWidth);
rect.setAttribute('y', i * cellHeight);
rect.setAttribute('width', cellWidth);
rect.setAttribute('height', cellHeight);
rect.setAttribute('fill', getColor(heatmapData[i][j]));
rect.setAttribute('data-value', heatmapData[i][j]);
rect.setAttribute('data-row', i);
rect.setAttribute('data-col', j);
rect.addEventListener('mouseover', (e) => {
const value = e.target.getAttribute('data-value');
const row = e.target.getAttribute('data-row');
const col = e.target.getAttribute('data-col');
tooltip.innerHTML = `Row: ${parseInt(row) + 1}, Col: ${parseInt(col) + 1}
Value: ${value}`;
tooltip.style.opacity = '1';
tooltip.style.left = `${e.clientX + 15}px`;
tooltip.style.top = `${e.clientY + 15}px`;
});
rect.addEventListener('mousemove', (e) => {
tooltip.style.left = `${e.clientX + 15}px`;
tooltip.style.top = `${e.clientY + 15}px`;
});
rect.addEventListener('mouseout', () => {
tooltip.style.opacity = '0';
});
svg.appendChild(rect);
}
}