Enhance map updates and component performance

- Updated API documentation to clarify the initial data message structure for real-time tile updates.
- Modified MapView component to load configuration and user data in parallel, improving map loading speed.
- Implemented asynchronous loading for markers after the map is visible, enhancing user experience.
- Introduced batching for tile updates to optimize rendering performance during map updates.
- Refactored character and marker creation functions to utilize dynamic Leaflet imports, improving modularity.
This commit is contained in:
2026-03-01 17:30:48 +03:00
parent 7bdaa6bfcc
commit 49af08c13f
9 changed files with 120 additions and 75 deletions

View File

@@ -271,9 +271,12 @@ onMounted(async () => {
const L = (await import('leaflet')).default
const [charactersData, mapsData] = await Promise.all([
// Load maps, characters, config and me in parallel so map can show sooner.
const [charactersData, mapsData, config, user] = await Promise.all([
api.getCharacters().then((d) => (Array.isArray(d) ? d : [])).catch(() => []),
api.getMaps().then((d) => (d && typeof d === 'object' ? d : {})).catch(() => ({})),
api.getConfig().catch(() => ({})) as Promise<ConfigResponse>,
api.me().catch(() => null) as Promise<MeResponse | null>,
])
const mapsList: MapInfo[] = []
@@ -295,9 +298,7 @@ onMounted(async () => {
maps.value = mapsList
mapsLoaded.value = true
const config = (await api.getConfig().catch(() => ({}))) as ConfigResponse
if (config?.title) document.title = config.title
const user = (await api.me().catch(() => null)) as MeResponse | null
auths.value = user?.auths ?? config?.auths ?? []
const initialMapId =
@@ -328,6 +329,7 @@ onMounted(async () => {
document.addEventListener('contextmenu', contextMenuHandler, true)
layersManager = createMapLayers({
L,
map: leafletMap,
markerLayer: mapInit.markerLayer,
layer: mapInit.layer,
@@ -382,16 +384,25 @@ onMounted(async () => {
}
}
// Show map as soon as canvas and layers are ready; markers load in background.
if (leafletMap) leafletMap.invalidateSize()
nextTick(() => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
if (leafletMap) leafletMap.invalidateSize()
mapReady.value = true
})
if (leafletMap) leafletMap.invalidateSize()
mapReady.value = true
})
})
intervalId = setInterval(() => {
// Markers load asynchronously after map is visible.
api.getMarkers().then((body) => {
layersManager!.updateMarkers(Array.isArray(body) ? body : [])
questGivers.value = layersManager!.getQuestGivers()
})
const CHARACTER_POLL_MS = 4000
const CHARACTER_POLL_MS_HIDDEN = 30000
function pollCharacters() {
api
.getCharacters()
.then((body) => {
@@ -400,24 +411,37 @@ onMounted(async () => {
players.value = layersManager!.getPlayers()
mapLive.value = list.some((c) => c.ownedByMe)
})
.catch(() => clearInterval(intervalId!))
}, 2000)
.catch(() => {
if (intervalId) clearInterval(intervalId)
intervalId = null
})
}
api.getMarkers().then((body) => {
layersManager!.updateMarkers(Array.isArray(body) ? body : [])
questGivers.value = layersManager!.getQuestGivers()
})
function startCharacterPoll() {
if (intervalId) clearInterval(intervalId)
const ms =
typeof document !== 'undefined' && document.visibilityState === 'hidden'
? CHARACTER_POLL_MS_HIDDEN
: CHARACTER_POLL_MS
pollCharacters()
intervalId = setInterval(pollCharacters, ms)
}
startCharacterPoll()
if (import.meta.client) {
document.addEventListener('visibilitychange', () => {
startCharacterPoll()
})
}
watch(mapLogic.state.showGridCoordinates, (v) => {
if (mapInit?.coordLayer) {
;(mapInit.coordLayer.options as { visible?: boolean }).visible = v
mapInit.coordLayer.setOpacity(v ? 1 : 0)
mapInit.coordLayer.redraw?.()
if (v && leafletMap) {
mapInit.coordLayer.bringToFront?.()
mapInit.coordLayer.redraw?.()
leafletMap.invalidateSize()
} else {
mapInit.coordLayer.redraw?.()
}
}
})