import React, { useRef, useEffect, useState } from 'react'; import { worldToPx } from '../../utils/coordinates'; import { apiFetch } from '../../api/client'; interface HeatmapPoint { ew: number; ns: number; intensity: number; } interface Props { imgW: number; imgH: number; enabled: boolean; } export const HeatmapCanvas: React.FC = ({ imgW, imgH, enabled }) => { const canvasRef = useRef(null); const [data, setData] = useState([]); useEffect(() => { if (!enabled) return; const fetch = async () => { try { const resp = await apiFetch<{ spawn_points: HeatmapPoint[] }>('/spawns/heatmap?hours=24&limit=50000'); setData(resp.spawn_points ?? []); } catch { /* ignore */ } }; fetch(); }, [enabled]); useEffect(() => { const canvas = canvasRef.current; if (!canvas || !enabled || data.length === 0 || imgW === 0) return; canvas.width = imgW; canvas.height = imgH; const ctx = canvas.getContext('2d'); if (!ctx) return; ctx.clearRect(0, 0, imgW, imgH); for (const point of data) { const { x, y } = worldToPx(point.ew, point.ns, imgW, imgH); const radius = Math.max(5, Math.min(12, 5 + Math.sqrt(point.intensity * 0.5))); const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius); gradient.addColorStop(0, `rgba(255, 0, 0, ${Math.min(0.9, point.intensity / 40)})`); gradient.addColorStop(0.6, `rgba(255, 100, 0, ${Math.min(0.4, point.intensity / 120)})`); gradient.addColorStop(1, 'rgba(255, 150, 0, 0)'); ctx.fillStyle = gradient; ctx.fillRect(x - radius, y - radius, radius * 2, radius * 2); } }, [data, imgW, imgH, enabled]); if (!enabled) return null; return ; };