2.2 KiB
2.2 KiB
Leaflet Map Integration for PWA Draft Games
When to add a map view
When a fantasy game's draft room needs to show geographic regions, a map view is more intuitive than a card grid. Use Leaflet.js + OpenStreetMap tiles for free, no-API-key map rendering.
CDN
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
Implementation pattern
- Map container: Single
<div id="leafletMap">in the HTML - On view switch: Destroy old instance (
window._leafletMap.remove()), create newL.map()centered on[20, 0]with zoom 2, minZoom 2, maxZoom 5 - Tiles:
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png') - Markers:
L.circleMarker([lat, lng], {radius, color, fillColor, fillOpacity})for each region - Popups:
marker.bindPopup(content)with region stats, name, and action button - Color coding: Available=teal/#0ea5e9, Your pick=amber/#f59e0b, Taken=red/#dc2626, Waiting=gray
- Toggle view: Map | List toggle button that calls
renderMapView()or shows card grid
Pitfalls
- Destroy before recreating: Always remove the previous Leaflet instance (
window._leafletMap.remove()) before initializing a new one. Multiple instances on the same div cause visual glitches. - Coordinate format: Leaflet uses
[lat, lng]in that order (not[lng, lat]like GeoJSON). - Dark theme: Override
leaflet-container,.leaflet-popup-content-wrapper, and.leaflet-control-zoomCSS to match dark theme. - Popup content: Leaflet's
bindPopup()accepts raw HTML — use it for region stats, action buttons, etc. Don't use custom overlay modals for map popups. - Mobile responsiveness: Leaflet handles viewport resizing automatically, but set
height: 500pxon the container and wrap in a responsive parent. - Prevent scrolling off-world: Without bounds locking, users can pan left/right until all markers disappear. Fix with:
Viscosity of 0.8 lets it feel natural (not a hard wall) while keeping markers in view.
const map = L.map('leafletMap', { maxBounds: [[-60, -180], [80, 180]], maxBoundsViscosity: 0.8, // 0 = hard wall, 1 = bounce });