- Updated .dockerignore to streamline build context by ensuring unnecessary files are excluded. - Refined CSS styles in leaflet-overrides.css to enhance visual consistency and user experience for map tooltips and popups. - Improved map initialization and update handling in useMapApi and useMapUpdates composables for better performance and reliability.
112 lines
3.2 KiB
TypeScript
112 lines
3.2 KiB
TypeScript
import type L from 'leaflet'
|
|
import { HnHCRS, HnHMaxZoom, HnHMinZoom, TileSize } from '~/lib/LeafletCustomTypes'
|
|
import { SmartTileLayer } from '~/lib/SmartTileLayer'
|
|
import type { MapInfo } from '~/types/api'
|
|
|
|
type SmartTileLayerInstance = InstanceType<typeof SmartTileLayer>
|
|
|
|
/** Known marker icon paths (without .png) to preload so markers render without broken images. */
|
|
const MARKER_ICON_PATHS = [
|
|
'gfx/terobjs/mm/custom',
|
|
'gfx/terobjs/mm/tower',
|
|
'gfx/terobjs/mm/village',
|
|
'gfx/terobjs/mm/dungeon',
|
|
'gfx/terobjs/mm/cave',
|
|
'gfx/terobjs/mm/settlement',
|
|
'gfx/invobjs/small/bush',
|
|
'gfx/invobjs/small/bumling',
|
|
]
|
|
|
|
/**
|
|
* Preloads marker icon images so they are in the browser cache before markers render.
|
|
* Call from client only. resolvePath should produce absolute URLs for static assets.
|
|
*/
|
|
export function preloadMarkerIcons(resolvePath: (path: string) => string): void {
|
|
if (import.meta.server) return
|
|
for (const base of MARKER_ICON_PATHS) {
|
|
const url = resolvePath(`${base}.png`)
|
|
const img = new Image()
|
|
img.src = url
|
|
}
|
|
}
|
|
|
|
export interface MapInitResult {
|
|
map: L.Map
|
|
layer: SmartTileLayerInstance
|
|
overlayLayer: SmartTileLayerInstance
|
|
markerLayer: L.LayerGroup
|
|
backendBase: string
|
|
}
|
|
|
|
export async function initLeafletMap(
|
|
element: HTMLElement,
|
|
mapsList: MapInfo[],
|
|
initialMapId: number
|
|
): Promise<MapInitResult> {
|
|
const L = (await import('leaflet')).default
|
|
|
|
const map = L.map(element, {
|
|
minZoom: HnHMinZoom,
|
|
maxZoom: HnHMaxZoom,
|
|
crs: HnHCRS,
|
|
attributionControl: false,
|
|
zoomControl: false,
|
|
inertia: true,
|
|
zoomAnimation: true,
|
|
fadeAnimation: true,
|
|
markerZoomAnimation: true,
|
|
})
|
|
|
|
const runtimeConfig = useRuntimeConfig()
|
|
const apiBase = (runtimeConfig.public.apiBase as string) ?? '/map/api'
|
|
const backendBase = apiBase.replace(/\/api\/?$/, '') || '/map'
|
|
const tileUrl = `${backendBase}/grids/{map}/{z}/{x}_{y}.png?{cache}`
|
|
|
|
const layer = new SmartTileLayer(tileUrl, {
|
|
minZoom: 1,
|
|
maxZoom: 6,
|
|
maxNativeZoom: 6,
|
|
zoomOffset: 0,
|
|
zoomReverse: true,
|
|
tileSize: TileSize,
|
|
updateWhenIdle: true,
|
|
keepBuffer: 4,
|
|
})
|
|
layer.map = initialMapId
|
|
layer.invalidTile =
|
|
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII='
|
|
layer.addTo(map)
|
|
|
|
const overlayLayer = new SmartTileLayer(tileUrl, {
|
|
minZoom: 1,
|
|
maxZoom: 6,
|
|
maxNativeZoom: 6,
|
|
zoomOffset: 0,
|
|
zoomReverse: true,
|
|
tileSize: TileSize,
|
|
opacity: 0.5,
|
|
updateWhenIdle: true,
|
|
keepBuffer: 4,
|
|
})
|
|
overlayLayer.map = -1
|
|
overlayLayer.invalidTile =
|
|
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='
|
|
overlayLayer.addTo(map)
|
|
|
|
const markerLayer = L.layerGroup()
|
|
markerLayer.addTo(map)
|
|
markerLayer.setZIndex(600)
|
|
|
|
const baseURL = useRuntimeConfig().app.baseURL ?? '/'
|
|
const markerIconPath = baseURL.endsWith('/') ? baseURL : baseURL + '/'
|
|
L.Icon.Default.imagePath = markerIconPath
|
|
|
|
const resolvePath = (path: string) => {
|
|
const p = path.startsWith('/') ? path : `/${path}`
|
|
return baseURL === '/' ? p : `${baseURL.replace(/\/$/, '')}${p}`
|
|
}
|
|
preloadMarkerIcons(resolvePath)
|
|
|
|
return { map, layer, overlayLayer, markerLayer, backendBase }
|
|
}
|