Browse Source

fix: rebuild route layers after map style/projection settle

setProjection can wipe custom sources; wait for style ready and
re-add route layers on style.load so edit polylines stay visible.
main
xiaosi 2 weeks ago
parent
commit
f22d49e0aa
  1. 132
      src/views/TasksView/TasksView.vue

132
src/views/TasksView/TasksView.vue

@ -288,6 +288,7 @@ let routeMapInstance = null
let routeMapAttached = false
let pendingStyleHandler = null
let pendingFitRoute = false
let styleRebuildHandler = null
const routeForm = reactive({ name: '', altitude: 50, speed: 10, turnMode: 'auto' })
const routePoints = ref([])
@ -711,6 +712,34 @@ async function waitForMapHostSize(el, tries = 40) {
return el.clientWidth > 0 && el.clientHeight > 0
}
async function waitForMapStyleReady(map, tries = 40) {
if (!map) return false
for (let i = 0; i < tries; i++) {
try {
if (map.isStyleLoaded?.() && map.loaded?.() !== false) {
// setProjection isStyleLoaded
await new Promise((r) => requestAnimationFrame(r))
if (map.isStyleLoaded?.()) return true
}
} catch (_) {
/* ignore */
}
await new Promise((r) => {
const onIdle = () => {
map.off?.('idle', onIdle)
r()
}
try {
map.once?.('idle', onIdle)
setTimeout(r, 50)
} catch (_) {
setTimeout(r, 50)
}
})
}
return !!map.isStyleLoaded?.()
}
function scheduleRouteSync({ fit = false } = {}) {
const map = routeMapInstance
if (!map) return
@ -721,47 +750,18 @@ function scheduleRouteSync({ fit = false } = {}) {
} catch (_) {
/* ignore */
}
// projection/style source
if (!ensureTaskLayers(map)) return
syncRouteLayer({ fit: shouldFit })
}
// rAF hidden / resize + fit
requestAnimationFrame(() => {
requestAnimationFrame(() => {
run(fit)
if (!fit) return
// ResizeObserver/ fit 0
requestAnimationFrame(() => run(true))
})
})
}
function onRouteMapClick(e) {
if (!routeMapAttached || !routeMapInstance) return
const hit = routeMapInstance.queryRenderedFeatures(e.point, {
layers: [WP_CIRCLE, WP_LABEL].filter((id) => routeMapInstance.getLayer(id)),
})
if (hit[0]?.properties?.index != null) {
openWaypointEditor(Number(hit[0].properties.index))
return
}
if (!validRouteDefaults()) return
const { lng, lat } = e.lngLat
routePoints.value.push({
longitude: lng,
latitude: lat,
altitude: Number(routeForm.altitude),
speed: Number(routeForm.speed),
turnMode: routeForm.turnMode,
})
routeCursor.value = formatCoord(lng, lat)
routeFormError.value = ''
syncRouteLayer()
}
function onRouteMapMove(e) {
if (!routeMapAttached) return
routeCursor.value = formatCoord(e.lngLat.lng, e.lngLat.lat)
}
async function attachRouteMap() {
await nextTick()
const host = routeMapEl.value
@ -774,7 +774,6 @@ async function attachRouteMap() {
return
}
// display:none 宿
await waitForMapHostSize(host)
routeMapInstance = map
@ -783,34 +782,65 @@ async function attachRouteMap() {
routeMapAttached = true
routeMapReady.value = false
// setProjection(mercator) style
await waitForMapStyleReady(map)
const boot = () => {
pendingStyleHandler = null
if (!routeMapAttached || routeMapInstance !== map) return
clearTaskLayers(map)
if (!ensureTaskLayers(map)) return
map.off('click', onRouteMapClick)
map.off('mousemove', onRouteMapMove)
map.on('click', onRouteMapClick)
map.on('mousemove', onRouteMapMove)
const c = map.getCenter()
routeCursor.value = formatCoord(c.lng, c.lat)
routeMapReady.value = true
const shouldFit = pendingFitRoute
pendingFitRoute = false
scheduleRouteSync({ fit: shouldFit })
// pendingFitRoute boot style fit
if (shouldFit) pendingFitRoute = false
scheduleRouteSync({ fit: shouldFit || routePoints.value.length > 0 })
}
if (pendingStyleHandler) {
map.off('load', pendingStyleHandler)
map.off('style.load', pendingStyleHandler)
pendingStyleHandler = null
}
if (styleRebuildHandler) {
map.off('style.load', styleRebuildHandler)
styleRebuildHandler = null
}
// style /projection线
styleRebuildHandler = () => {
if (!routeMapAttached || routeMapInstance !== map) return
boot()
}
map.on('style.load', styleRebuildHandler)
if (map.isStyleLoaded()) {
boot()
} else {
pendingStyleHandler = boot
pendingStyleHandler = () => {
pendingStyleHandler = null
boot()
}
map.once('load', pendingStyleHandler)
map.once('style.load', pendingStyleHandler)
}
}
function teardownRouteMap() {
if (routeMapInstance && pendingStyleHandler) {
routeMapInstance.off('load', pendingStyleHandler)
routeMapInstance.off('style.load', pendingStyleHandler)
pendingStyleHandler = null
}
if (routeMapInstance && styleRebuildHandler) {
routeMapInstance.off('style.load', styleRebuildHandler)
styleRebuildHandler = null
}
if (routeMapInstance) {
routeMapInstance.off('click', onRouteMapClick)
routeMapInstance.off('mousemove', onRouteMapMove)
@ -818,7 +848,6 @@ function teardownRouteMap() {
}
if (routeMapAttached) {
mapHelper.restoreMapContainer()
// globe + pitch 0 3D pitch 45
try {
mapHelper.getMap()?.setProjection?.('globe')
} catch (_) {
@ -831,6 +860,35 @@ function teardownRouteMap() {
pendingFitRoute = false
}
function onRouteMapClick(e) {
if (!routeMapAttached || !routeMapInstance) return
const hit = routeMapInstance.queryRenderedFeatures(e.point, {
layers: [WP_CIRCLE, WP_LABEL].filter((id) => routeMapInstance.getLayer(id)),
})
if (hit[0]?.properties?.index != null) {
openWaypointEditor(Number(hit[0].properties.index))
return
}
if (!validRouteDefaults()) return
const { lng, lat } = e.lngLat
routePoints.value.push({
longitude: lng,
latitude: lat,
altitude: Number(routeForm.altitude),
speed: Number(routeForm.speed),
turnMode: routeForm.turnMode,
})
routeCursor.value = formatCoord(lng, lat)
routeFormError.value = ''
syncRouteLayer()
}
function onRouteMapMove(e) {
if (!routeMapAttached) return
routeCursor.value = formatCoord(e.lngLat.lng, e.lngLat.lat)
}
function openRouteEditor() {
resetRouteEditor()
routeEditorOpen.value = true

Loading…
Cancel
Save