Move marker set markers into single component. Start marker update handling.

This commit is contained in:
James Lyne 2020-12-14 00:27:49 +00:00
parent 52e8e5c6cf
commit 6249eb904b
4 changed files with 155 additions and 11 deletions

View File

@ -1,8 +1,8 @@
<template> <template>
<GenericMarker v-for="[id, marker] in markerSet.markers" :options="marker" :key="id" :layer-group="layerGroup"></GenericMarker>
<Areas :layer-group="layerGroup" :set="markerSet"></Areas> <Areas :layer-group="layerGroup" :set="markerSet"></Areas>
<Circles :layer-group="layerGroup" :set="markerSet"></Circles> <Circles :layer-group="layerGroup" :set="markerSet"></Circles>
<Lines :layer-group="layerGroup" :set="markerSet"></Lines> <Lines :layer-group="layerGroup" :set="markerSet"></Lines>
<Markers :layer-group="layerGroup" :set="markerSet"></Markers>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -10,18 +10,18 @@ import {defineComponent, computed} from "@vue/runtime-core";
import {useStore} from "@/store"; import {useStore} from "@/store";
import {LayerGroup} from 'leaflet'; import {LayerGroup} from 'leaflet';
import {DynmapMarkerSet} from "@/dynmap"; import {DynmapMarkerSet} from "@/dynmap";
import GenericMarker from "@/components/map/marker/GenericMarker.vue";
import Areas from "@/components/map/vector/Areas.vue"; import Areas from "@/components/map/vector/Areas.vue";
import Circles from "@/components/map/vector/Circles.vue"; import Circles from "@/components/map/vector/Circles.vue";
import Lines from "@/components/map/vector/Lines.vue"; import Lines from "@/components/map/vector/Lines.vue";
import Markers from "@/components/map/vector/Markers.vue";
import DynmapMap from "@/leaflet/DynmapMap"; import DynmapMap from "@/leaflet/DynmapMap";
export default defineComponent({ export default defineComponent({
components: { components: {
GenericMarker,
Areas, Areas,
Circles, Circles,
Lines, Lines,
Markers,
}, },
props: { props: {

View File

@ -0,0 +1,105 @@
<script lang="ts">
import {defineComponent, computed, onMounted, onUnmounted, watch} from "@vue/runtime-core";
import {LayerGroup, Marker} from 'leaflet';
import {useStore} from "@/store";
import {DynmapMarker, DynmapMarkerSet} from "@/dynmap";
import {ActionTypes} from "@/store/action-types";
import {createMarker, updateMarker} from "@/util/markers";
export default defineComponent({
props: {
set: {
type: Object as () => DynmapMarkerSet,
required: true,
},
layerGroup: {
type: Object as () => LayerGroup,
required: true
}
},
setup(props) {
let updateFrame = 0;
const store = useStore(),
currentProjection = computed(() => store.state.currentProjection),
pendingUpdates = computed(() => {
const markerSetUpdates = store.state.pendingSetUpdates.get(props.set.id);
return markerSetUpdates && markerSetUpdates.markerUpdates.length;
}),
layers = Object.freeze(new Map()) as Map<string, Marker>,
createMarkers = () => {
const projection = currentProjection.value;
props.set.markers.forEach((marker: DynmapMarker, id: string) => {
const layer = createMarker(marker, projection);
layers.set(id, layer);
props.layerGroup.addLayer(layer);
});
},
deleteMarker = (id: string) => {
let marker = layers.get(id) as Marker;
if(!marker) {
return;
}
marker.remove();
layers.delete(id);
},
handlePendingUpdates = () => {
useStore().dispatch(ActionTypes.POP_MARKER_UPDATES, {
markerSet: props.set.id,
amount: 10,
}).then(updates => {
const projection = currentProjection.value;
for(const update of updates) {
if(update.removed) {
console.log(`Deleting marker ${update.id}`);
deleteMarker(update.id);
} else {
console.log(`Updating/creating marker ${update.id}`);
layers.set(update.id, updateMarker(layers.get(update.id), update.payload as DynmapMarker, projection));
}
}
if(pendingUpdates.value) {
console.log('More updates left, scheduling frame');
// eslint-disable-next-line no-unused-vars
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
} else {
updateFrame = 0;
}
});
};
//FIXME: Prevent unnecessary repositioning when changing worlds
watch(currentProjection, () => {
const projection = currentProjection.value;
for (const [id, marker] of props.set.markers) {
updateMarker(layers.get(id), marker, projection);
}
});
watch(pendingUpdates, (newValue, oldValue) => {
if(newValue && newValue > 0 && oldValue === 0 && !updateFrame) {
updateFrame = requestAnimationFrame(() => handlePendingUpdates());
}
});
onMounted(() => createMarkers());
onUnmounted(() => updateFrame && cancelAnimationFrame(updateFrame));
},
render() {
return null;
}
});
</script>

View File

@ -7,6 +7,15 @@ export interface DynmapIconOptions extends DivIconOptions {
isHtml?: boolean; isHtml?: boolean;
} }
const markerContainer: HTMLDivElement = document.createElement('div');
markerContainer.className = 'marker';
const markerIcon: HTMLImageElement = document.createElement('img');
markerIcon.className = 'marker__icon';
const markerLabel: HTMLSpanElement = document.createElement('span');
markerLabel.className = 'marker__label';
export class DynmapIcon extends DivIcon { export class DynmapIcon extends DivIcon {
static defaultOptions: DynmapIconOptions = { static defaultOptions: DynmapIconOptions = {
icon: 'default', icon: 'default',
@ -29,18 +38,23 @@ export class DynmapIcon extends DivIcon {
DomUtil.remove(oldIcon); DomUtil.remove(oldIcon);
} }
const div = document.createElement('div'), const div = markerContainer.cloneNode(false) as HTMLDivElement,
img = document.createElement('img'), img = markerIcon.cloneNode(false) as HTMLImageElement,
label = document.createElement('span'), label = markerLabel.cloneNode(false) as HTMLSpanElement,
url = `${window.config.url.markers}_markers_/${this.options.icon}.png`, url = `${window.config.url.markers}_markers_/${this.options.icon}.png`,
size = point(this.options.iconSize as PointExpression); size = point(this.options.iconSize as PointExpression);
const sizeClass = [size.x, size.y].join('x'); const sizeClass = [size.x, size.y].join('x');
img.className = `marker__icon marker__icon--${sizeClass}`; img.width = size.x;
img.height = size.y;
img.src = url; img.src = url;
label.className = this.options.showLabel ? 'marker__label marker__label--show' : 'marker__label'; if(this.options.showLabel) {
label.classList.add('marker__label--show');
}
label.classList.add(/*'markerName_' + set.id,*/ `marker__label--${sizeClass}`); label.classList.add(/*'markerName_' + set.id,*/ `marker__label--${sizeClass}`);
if (this.options.isHtml) { if (this.options.isHtml) {
@ -54,15 +68,12 @@ export class DynmapIcon extends DivIcon {
div.appendChild(img); div.appendChild(img);
div.appendChild(label); div.appendChild(label);
div.classList.add('marker'); div.classList.add('marker');
if(this.options.className) { if(this.options.className) {
div.classList.add(this.options.className); div.classList.add(this.options.className);
} }
console.log(div.className);
return div; return div;
} }
} }

28
src/util/markers.ts Normal file
View File

@ -0,0 +1,28 @@
import {Marker} from "leaflet";
import {DynmapMarker} from "@/dynmap";
import {DynmapIcon} from "@/leaflet/icon/DynmapIcon";
import {DynmapProjection} from "@/leaflet/projection/DynmapProjection";
export const createMarker = (options: DynmapMarker, projection: DynmapProjection): Marker => {
return new Marker(projection.locationToLatLng(options.location), {
icon: new DynmapIcon({
icon: options.icon,
label: options.label,
iconSize: options.dimensions,
showLabel: false,
isHtml: options.isHTML,
}),
// maxZoom: this.options.maxZoom,
// minZoom: this.options.minZoom,
});
};
export const updateMarker = (marker: Marker | undefined, options: DynmapMarker, projection: DynmapProjection): Marker => {
if (!marker) {
return createMarker(options, projection);
}
//TODO
return marker;
};