'use client'; import { useRef, useEffect, useCallback } from 'react'; import { useLocale } from './LocaleProvider'; import { ts } from '@/lib/i18n'; export default function Infographics() { const { locale } = useLocale(); const phasesCanvasRef = useRef(null); const tidesCanvasRef = useRef(null); const drawPhasesCycle = useCallback(() => { const canvas = phasesCanvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); if (!ctx) return; const w = canvas.width; const h = canvas.height; const cx = w / 2; const cy = h / 2; const radius = Math.min(cx, cy) - 60; ctx.clearRect(0, 0, w, h); // Background ctx.fillStyle = '#0a0a1a'; ctx.fillRect(0, 0, w, h); // Orbit circle ctx.beginPath(); ctx.arc(cx, cy, radius, 0, Math.PI * 2); ctx.strokeStyle = 'rgba(99, 102, 241, 0.2)'; ctx.lineWidth = 2; ctx.setLineDash([5, 5]); ctx.stroke(); ctx.setLineDash([]); // Earth at center ctx.beginPath(); ctx.arc(cx, cy, 25, 0, Math.PI * 2); const earthGrad = ctx.createRadialGradient(cx - 5, cy - 5, 0, cx, cy, 25); earthGrad.addColorStop(0, '#4da6ff'); earthGrad.addColorStop(1, '#1a5276'); ctx.fillStyle = earthGrad; ctx.fill(); ctx.fillStyle = '#fff'; ctx.font = 'bold 10px system-ui'; ctx.textAlign = 'center'; ctx.fillText('🌍', cx, cy + 4); // Sun direction arrow ctx.fillStyle = 'rgba(251, 191, 36, 0.6)'; ctx.font = '12px system-ui'; ctx.fillText('β˜€οΈ Sun β†’', w - 60, 30); // Sun light gradient from right const sunGrad = ctx.createLinearGradient(w, 0, 0, 0); sunGrad.addColorStop(0, 'rgba(251, 191, 36, 0.03)'); sunGrad.addColorStop(1, 'rgba(251, 191, 36, 0)'); ctx.fillStyle = sunGrad; ctx.fillRect(0, 0, w, h); // Moon phases around orbit const phases = [ { angle: 0, emoji: 'πŸŒ‘', label: ts('new_moon', locale), illum: '0%' }, { angle: Math.PI / 4, emoji: 'πŸŒ’', label: ts('waxing_crescent', locale), illum: '25%' }, { angle: Math.PI / 2, emoji: 'πŸŒ“', label: ts('first_quarter', locale), illum: '50%' }, { angle: 3 * Math.PI / 4, emoji: 'πŸŒ”', label: ts('waxing_gibbous', locale), illum: '75%' }, { angle: Math.PI, emoji: 'πŸŒ•', label: ts('full_moon', locale), illum: '100%' }, { angle: 5 * Math.PI / 4, emoji: 'πŸŒ–', label: ts('waning_gibbous', locale), illum: '75%' }, { angle: 3 * Math.PI / 2, emoji: 'πŸŒ—', label: ts('last_quarter', locale), illum: '50%' }, { angle: 7 * Math.PI / 4, emoji: '🌘', label: ts('waning_crescent', locale), illum: '25%' }, ]; phases.forEach((p) => { const x = cx + Math.sin(p.angle) * radius; const y = cy - Math.cos(p.angle) * radius; // Moon circle background ctx.beginPath(); ctx.arc(x, y, 22, 0, Math.PI * 2); ctx.fillStyle = 'rgba(15, 15, 35, 0.9)'; ctx.fill(); ctx.strokeStyle = 'rgba(99, 102, 241, 0.3)'; ctx.lineWidth = 1; ctx.stroke(); // Emoji ctx.fillStyle = '#fff'; ctx.font = '22px system-ui'; ctx.textAlign = 'center'; ctx.fillText(p.emoji, x, y + 7); // Label const labelX = cx + Math.sin(p.angle) * (radius + 42); const labelY = cy - Math.cos(p.angle) * (radius + 42); ctx.fillStyle = 'rgba(232, 232, 240, 0.7)'; ctx.font = '11px system-ui'; ctx.fillText(String(p.label), labelX, labelY); ctx.fillStyle = 'rgba(129, 140, 248, 0.7)'; ctx.font = '9px system-ui'; ctx.fillText(p.illum, labelX, labelY + 14); }); // Title ctx.fillStyle = '#818cf8'; ctx.font = 'bold 14px system-ui'; ctx.textAlign = 'center'; ctx.fillText('29.53 days', cx, cy + 45); ctx.fillStyle = 'rgba(255,255,255,0.4)'; ctx.font = '10px system-ui'; ctx.fillText('Synodic Month', cx, cy + 58); }, [locale]); const drawTidesChart = useCallback(() => { const canvas = tidesCanvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); if (!ctx) return; const w = canvas.width; const h = canvas.height; const pad = { top: 40, right: 20, bottom: 50, left: 50 }; const chartW = w - pad.left - pad.right; const chartH = h - pad.top - pad.bottom; ctx.clearRect(0, 0, w, h); ctx.fillStyle = '#0a0a1a'; ctx.fillRect(0, 0, w, h); // Title ctx.fillStyle = '#818cf8'; ctx.font = 'bold 14px system-ui'; ctx.textAlign = 'center'; ctx.fillText(locale === 'fr' ? 'Influence lunaire sur les marΓ©es' : 'Lunar Influence on Tides', w / 2, 25); // Axes ctx.strokeStyle = 'rgba(255,255,255,0.2)'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(pad.left, pad.top); ctx.lineTo(pad.left, h - pad.bottom); ctx.lineTo(w - pad.right, h - pad.bottom); ctx.stroke(); // Y-axis label ctx.save(); ctx.translate(15, h / 2); ctx.rotate(-Math.PI / 2); ctx.fillStyle = 'rgba(255,255,255,0.4)'; ctx.font = '11px system-ui'; ctx.textAlign = 'center'; ctx.fillText(locale === 'fr' ? 'Hauteur des marΓ©es (m)' : 'Tide Height (m)', 0, 0); ctx.restore(); // Generate tide data (29.53 days cycle) const days = 30; const points: { x: number; y: number }[] = []; for (let d = 0; d <= days; d += 0.5) { const phaseAngle = (d / 29.53) * Math.PI * 2; // Spring tides at new/full moon (0 and Ο€), neap tides at quarters const springNeap = Math.cos(2 * phaseAngle); // Semi-diurnal tide variation const semiDiurnal = Math.sin(d * Math.PI * 2 * 2); const tideHeight = 1.0 + springNeap * 0.8 + semiDiurnal * 0.3; const x = pad.left + (d / days) * chartW; const y = pad.top + chartH - (tideHeight / 2.5) * chartH; points.push({ x, y }); } // Fill area under curve ctx.beginPath(); ctx.moveTo(points[0].x, h - pad.bottom); points.forEach(p => ctx.lineTo(p.x, p.y)); ctx.lineTo(points[points.length - 1].x, h - pad.bottom); ctx.closePath(); const fillGrad = ctx.createLinearGradient(0, pad.top, 0, h - pad.bottom); fillGrad.addColorStop(0, 'rgba(99, 102, 241, 0.3)'); fillGrad.addColorStop(1, 'rgba(99, 102, 241, 0.02)'); ctx.fillStyle = fillGrad; ctx.fill(); // Line ctx.beginPath(); points.forEach((p, i) => { if (i === 0) ctx.moveTo(p.x, p.y); else ctx.lineTo(p.x, p.y); }); ctx.strokeStyle = '#818cf8'; ctx.lineWidth = 2; ctx.stroke(); // Phase markers const phaseMarkers = [ { day: 0, emoji: 'πŸŒ‘', label: ts('new_moon', locale) }, { day: 7.38, emoji: 'πŸŒ“', label: ts('first_quarter', locale) }, { day: 14.77, emoji: 'πŸŒ•', label: ts('full_moon', locale) }, { day: 22.15, emoji: 'πŸŒ—', label: ts('last_quarter', locale) }, { day: 29.53, emoji: 'πŸŒ‘', label: ts('new_moon', locale) }, ]; phaseMarkers.forEach(m => { const x = pad.left + (m.day / days) * chartW; // Vertical line ctx.strokeStyle = 'rgba(255,255,255,0.1)'; ctx.setLineDash([3, 3]); ctx.beginPath(); ctx.moveTo(x, pad.top); ctx.lineTo(x, h - pad.bottom); ctx.stroke(); ctx.setLineDash([]); // Emoji ctx.font = '16px system-ui'; ctx.textAlign = 'center'; ctx.fillText(m.emoji, x, h - pad.bottom + 20); // Label ctx.fillStyle = 'rgba(255,255,255,0.4)'; ctx.font = '9px system-ui'; ctx.fillText(String(m.label).substring(0, 12), x, h - pad.bottom + 38); }); // Spring/Neap labels ctx.fillStyle = 'rgba(251, 191, 36, 0.7)'; ctx.font = 'bold 10px system-ui'; const springX1 = pad.left + (0 / days) * chartW + 20; ctx.fillText(locale === 'fr' ? 'Vives-eaux' : 'Spring Tide', springX1 + 30, pad.top + 15); const springX2 = pad.left + (14.77 / days) * chartW; ctx.fillText(locale === 'fr' ? 'Vives-eaux' : 'Spring Tide', springX2, pad.top + 15); ctx.fillStyle = 'rgba(34, 197, 94, 0.7)'; const neapX1 = pad.left + (7.38 / days) * chartW; ctx.fillText(locale === 'fr' ? 'Mortes-eaux' : 'Neap Tide', neapX1, pad.top + 15); const neapX2 = pad.left + (22.15 / days) * chartW; ctx.fillText(locale === 'fr' ? 'Mortes-eaux' : 'Neap Tide', neapX2, pad.top + 15); }, [locale]); useEffect(() => { drawPhasesCycle(); drawTidesChart(); }, [drawPhasesCycle, drawTidesChart]); return (

{ts('infographics_title', locale)}

{ts('infographics_subtitle', locale)}

); }