feat(weather): add real-time animated cloud and precipitation radar layer, integrate early warning badges, refine showcase text, and remove floating CTA button
This commit is contained in:
@@ -331,8 +331,62 @@ const MapComponent: React.FC<MapProps> = ({
|
||||
}
|
||||
};
|
||||
fetchWeather();
|
||||
|
||||
// 2. Fetch RainViewer Animated Cloud & Rain Radar
|
||||
let radarTimer: any = null;
|
||||
const fetchRadar = async () => {
|
||||
try {
|
||||
const res = await fetch('https://api.rainviewer.com/public/weather-maps.json');
|
||||
const data = await res.json();
|
||||
const host = data.host || 'https://tilecache.rainviewer.com';
|
||||
const frames = data.radar?.past || [];
|
||||
if (frames.length > 0 && map.current && map.current.isStyleLoaded()) {
|
||||
let currentIdx = frames.length - 1;
|
||||
const getTileUrl = (i: number) => `${host}${frames[i].path}`;
|
||||
|
||||
if (!map.current.getSource('weather-radar-source')) {
|
||||
map.current.addSource('weather-radar-source', {
|
||||
type: 'raster',
|
||||
tiles: [getTileUrl(currentIdx)],
|
||||
tileSize: 256
|
||||
});
|
||||
|
||||
map.current.addLayer({
|
||||
id: 'weather-radar-layer',
|
||||
type: 'raster',
|
||||
source: 'weather-radar-source',
|
||||
paint: {
|
||||
'raster-opacity': 0.72,
|
||||
'raster-fade-duration': 250
|
||||
}
|
||||
}, map.current.getLayer('weather-city-circles') ? 'weather-city-circles' : undefined);
|
||||
} else {
|
||||
map.current.setLayoutProperty('weather-radar-layer', 'visibility', 'visible');
|
||||
}
|
||||
|
||||
// Animate through radar frames every 1200ms
|
||||
if (frames.length > 1) {
|
||||
radarTimer = setInterval(() => {
|
||||
if (!map.current || !map.current.getSource('weather-radar-source')) return;
|
||||
currentIdx = (currentIdx + 1) % frames.length;
|
||||
const src = map.current.getSource('weather-radar-source') as any;
|
||||
if (src && src.setTiles) {
|
||||
src.setTiles([getTileUrl(currentIdx)]);
|
||||
}
|
||||
}, 1200);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Radar fetch failed', err);
|
||||
}
|
||||
};
|
||||
fetchRadar();
|
||||
|
||||
return () => {
|
||||
if (radarTimer) clearInterval(radarTimer);
|
||||
};
|
||||
} else {
|
||||
const weatherLayers = ['weather-city-circles', 'weather-city-icons', 'weather-wind-arrows', 'weather-wind-speed'];
|
||||
const weatherLayers = ['weather-city-circles', 'weather-city-icons', 'weather-wind-arrows', 'weather-wind-speed', 'weather-radar-layer'];
|
||||
weatherLayers.forEach(layerId => {
|
||||
if (map.current && map.current.getLayer(layerId)) {
|
||||
map.current.setLayoutProperty(layerId, 'visibility', 'none');
|
||||
|
||||
@@ -178,6 +178,25 @@ const WeatherPanel: React.FC<WeatherPanelProps> = ({ visible, selectedCity, aler
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
background: 'rgba(56, 189, 248, 0.12)',
|
||||
border: '1px solid rgba(56, 189, 248, 0.25)',
|
||||
padding: '6px 10px',
|
||||
borderRadius: '8px',
|
||||
marginBottom: '10px',
|
||||
fontSize: '0.78rem',
|
||||
color: '#38bdf8'
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '6px', fontWeight: 700 }}>
|
||||
<span style={{ display: 'inline-block', width: 8, height: 8, borderRadius: '50%', background: '#22c55e', boxShadow: '0 0 8px #22c55e' }}></span>
|
||||
رادار حركة السحب والأمطار (Live Radar)
|
||||
</div>
|
||||
<span style={{ fontSize: '0.72rem', color: '#bae6fd' }}>تحديث متحرك</span>
|
||||
</div>
|
||||
|
||||
{!selectedCity && (
|
||||
<div style={{ textAlign: 'center', padding: '16px 8px', color: '#94a3b8', fontSize: '0.85rem', background: 'rgba(255,255,255,0.03)', borderRadius: '8px', marginBottom: '10px' }}>
|
||||
💡 اضغط على أي مدينة على الخريطة لعرض تفاصيلها وتوقعات 7 أيام
|
||||
|
||||
Reference in New Issue
Block a user