Rewrite handling of pan/zoom across map changes. Fixes various timing issues.

This commit is contained in:
James Lyne 2021-01-27 00:11:08 +00:00
parent 572fc64b7d
commit d79a5a0a36

View File

@ -81,9 +81,9 @@ export default defineComponent({
followTarget = computed(() => store.state.followTarget), followTarget = computed(() => store.state.followTarget),
panTarget = computed(() => store.state.panTarget), panTarget = computed(() => store.state.panTarget),
//Animation frame callbacks for panning after projection change //Location and zoom to pan to upon next projection change
followFrame = ref(0), scheduledPan = ref<Coordinate|null>(null),
worldChangeFrame = ref(0); scheduledZoom = ref<number|null>(null);
return { return {
leaflet, leaflet,
@ -98,12 +98,12 @@ export default defineComponent({
logoControls, logoControls,
followTarget, followTarget,
panTarget, panTarget,
followFrame,
worldChangeFrame,
mapBackground, mapBackground,
currentWorld, currentWorld,
currentMap, currentMap,
currentProjection currentProjection,
scheduledPan,
scheduledZoom
} }
}, },
@ -128,24 +128,28 @@ export default defineComponent({
}, },
currentProjection(newValue, oldValue) { currentProjection(newValue, oldValue) {
if(this.leaflet && newValue && oldValue) { if(this.leaflet && newValue && oldValue) {
this.leaflet.panTo(newValue.locationToLatLng(oldValue.latLngToLocation(this.leaflet.getCenter(), 64)), { const panTarget = this.scheduledPan || oldValue.latLngToLocation(this.leaflet.getCenter(), 64);
if(this.scheduledZoom) {
this.leaflet!.setZoom(this.scheduledZoom, {
animate: false,
});
}
this.leaflet.panTo(newValue.locationToLatLng(panTarget), {
animate: false, animate: false,
noMoveStart: true, noMoveStart: true,
}); });
this.scheduledZoom = null;
this.scheduledPan = null;
} }
}, },
currentWorld(newValue, oldValue) { currentWorld(newValue, oldValue) {
const store = useStore(); const store = useStore();
//Cancel any pending pan frame
if(this.worldChangeFrame) {
cancelAnimationFrame(this.worldChangeFrame);
this.worldChangeFrame = 0;
}
if(newValue) { if(newValue) {
let location: Coordinate; let location: Coordinate | null = this.scheduledPan;
let zoom: number;
store.dispatch(ActionTypes.GET_MARKER_SETS, undefined); store.dispatch(ActionTypes.GET_MARKER_SETS, undefined);
@ -166,34 +170,14 @@ export default defineComponent({
} }
if(!oldValue) { if(!oldValue) {
zoom = typeof store.state.parsedUrl.zoom !== 'undefined' ? this.scheduledZoom = typeof store.state.parsedUrl.zoom !== 'undefined' ?
store.state.parsedUrl.zoom : store.state.configuration.defaultZoom; store.state.parsedUrl.zoom : store.state.configuration.defaultZoom;
} }
//Delay the pan by a frame, to allow the projection to be updated by the new world //Set pan location for when the projection changes
this.worldChangeFrame = requestAnimationFrame(() => { this.scheduledPan = location;
this.leaflet!.panTo(this.currentProjection.locationToLatLng(location), {
animate: false,
noMoveStart: true,
});
this.leaflet!.setZoom(zoom, {
animate: false,
});
});
} }
}, }
configuration: {
handler(newValue) {
if(this.leaflet) {
this.leaflet.setZoom(newValue.defaultZoom, {
animate: false,
noMoveStart: true,
});
}
},
deep: true,
},
}, },
mounted() { mounted() {
@ -230,13 +214,10 @@ export default defineComponent({
methods: { methods: {
updateFollow(player: DynmapPlayer, newFollow: boolean) { updateFollow(player: DynmapPlayer, newFollow: boolean) {
const store = useStore(), const store = useStore(),
followMapName = store.state.configuration.followMap,
currentWorld = store.state.currentWorld; currentWorld = store.state.currentWorld;
//Cancel any pending pan frame let targetWorld = null;
if(this.followFrame) {
cancelAnimationFrame(this.followFrame);
this.followFrame = 0;
}
if(!this.leaflet) { if(!this.leaflet) {
console.warn(`Cannot follow ${player.account}. Map not yet initialized.`); console.warn(`Cannot follow ${player.account}. Map not yet initialized.`);
@ -254,33 +235,38 @@ export default defineComponent({
} }
if(!currentWorld || currentWorld.name !== player.location.world) { if(!currentWorld || currentWorld.name !== player.location.world) {
const followMapName = store.state.configuration.followMap, targetWorld = store.state.worlds.get(player.location.world);
world = store.state.worlds.get(player.location.world); } else {
targetWorld = currentWorld;
if(!world) {
console.warn(`Cannot follow ${player.account}. Player isn't in a known world.`);
return;
}
let map = followMapName && world.maps.has(followMapName)
? world.maps.get(followMapName)
: world.maps.entries().next().value[1]
if(map !== store.state.currentMap) {
console.log(`Switching map to match player ${world.name} ${map.name}`);
store.commit(MutationTypes.SET_CURRENT_MAP, {worldName: world.name, mapName: map.name});
}
} }
//Delay the pan by a frame, to allow the projection to be updated by the new world if (!targetWorld) {
this.followFrame = requestAnimationFrame(() => { console.warn(`Cannot follow ${player.account}. Player isn't in a known world.`);
return;
}
let map = followMapName && targetWorld.maps.has(followMapName)
? targetWorld.maps.get(followMapName)
: targetWorld.maps.entries().next().value[1]
if(map !== store.state.currentMap) {
this.scheduledPan = player.location;
if(newFollow) {
console.log(`Setting zoom for new follow ${store.state.configuration.followZoom}`);
this.scheduledZoom = store.state.configuration.followZoom;
}
console.log(`Switching map to match player ${targetWorld.name} ${map.name}`);
store.commit(MutationTypes.SET_CURRENT_MAP, {worldName: targetWorld.name, mapName: map.name});
} else {
this.leaflet!.panTo(store.state.currentProjection.locationToLatLng(player.location)); this.leaflet!.panTo(store.state.currentProjection.locationToLatLng(player.location));
if(newFollow) { if(newFollow) {
console.log(`Setting zoom for new follow ${store.state.configuration.followZoom}`); console.log(`Setting zoom for new follow ${store.state.configuration.followZoom}`);
this.leaflet!.setZoom(store.state.configuration.followZoom); this.leaflet!.setZoom(store.state.configuration.followZoom);
} }
}) }
} }
} }
}) })