Added flyTo support ; targets players
The implementation forces Vue to attach vanilla event handlers, because their VDom doesn't bubble up and there's no way for it to easily communicate with Astro components. Nanostores aren't an option because .astro files are rendered on the server, so the store can't be used client side (would be nice if it did tho!!!)
This commit is contained in:
@@ -12,40 +12,40 @@ const { markers, players } = $world.get()
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script lang="ts" define:vars={{ markers, players }}>
|
<script lang="ts" define:vars={{ markers, players }}>
|
||||||
const mapHeight = 15168;
|
const mapHeight = 15168;
|
||||||
const mapWidth = 14658;
|
const mapWidth = 14658;
|
||||||
|
|
||||||
const map = L.map('world', {
|
const map = L.map('world', {
|
||||||
crs: L.CRS.Simple,
|
crs: L.CRS.Simple,
|
||||||
maxZoom: 12,
|
maxZoom: 12,
|
||||||
minZoom: 2.5,
|
minZoom: 2.5,
|
||||||
zoom: 2.5,
|
zoom: 2.5,
|
||||||
zoomSnap: 0,
|
zoomSnap: 0,
|
||||||
zoomControl: false
|
zoomControl: false
|
||||||
});
|
});
|
||||||
|
|
||||||
L.control.zoom({
|
L.control.zoom({
|
||||||
position: 'bottomright',
|
position: 'bottomright',
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
// Layers
|
// Layers
|
||||||
const layer = L.tileLayer.zoomify('/zoomify/alliance-kaldelienne/{g}/{z}-{x}-{y}.jpg', {
|
const layer = L.tileLayer.zoomify('/zoomify/alliance-kaldelienne/{g}/{z}-{x}-{y}.jpg', {
|
||||||
width: mapWidth,
|
width: mapWidth,
|
||||||
height: mapHeight,
|
height: mapHeight,
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
const layerGroups = {
|
const layerGroups = {
|
||||||
players: L.layerGroup(),
|
players: L.layerGroup(),
|
||||||
capitals: L.layerGroup(),
|
capitals: L.layerGroup(),
|
||||||
cities: L.layerGroup(),
|
cities: L.layerGroup(),
|
||||||
towns: L.layerGroup(),
|
towns: L.layerGroup(),
|
||||||
landmarks: L.layerGroup(),
|
landmarks: L.layerGroup(),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build all markers and their related info
|
* Build all markers and their related info
|
||||||
*/
|
*/
|
||||||
for (let i = 0; i < markers.length; i++) {
|
for (let i = 0; i < markers.length; i++) {
|
||||||
const m = markers[i];
|
const m = markers[i];
|
||||||
|
|
||||||
const coords = [m.markerCoords.y, m.markerCoords.x];
|
const coords = [m.markerCoords.y, m.markerCoords.x];
|
||||||
@@ -107,12 +107,12 @@ const { markers, players } = $world.get()
|
|||||||
}
|
}
|
||||||
|
|
||||||
marker.addTo(layerGroups[m.group])
|
marker.addTo(layerGroups[m.group])
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add player's position marker
|
* Add player's position marker
|
||||||
*/
|
*/
|
||||||
if ( players ) {
|
if ( players ) {
|
||||||
const playersPosition = [players.markerCoords.y, players.markerCoords.x];
|
const playersPosition = [players.markerCoords.y, players.markerCoords.x];
|
||||||
|
|
||||||
const playerIcon = L.icon({
|
const playerIcon = L.icon({
|
||||||
@@ -126,20 +126,20 @@ const { markers, players } = $world.get()
|
|||||||
|
|
||||||
const playerMarker = L.marker(playersPosition, { icon: playerIcon }).addTo(map)
|
const playerMarker = L.marker(playersPosition, { icon: playerIcon }).addTo(map)
|
||||||
playerMarker.addTo(layerGroups['players'])
|
playerMarker.addTo(layerGroups['players'])
|
||||||
}
|
}
|
||||||
|
|
||||||
map.fitBounds(layer.getBounds());
|
map.fitBounds(layer.getBounds());
|
||||||
|
|
||||||
// L.control.layers({ "base": layer }, layerGroups).addTo(map);
|
// L.control.layers({ "base": layer }, layerGroups).addTo(map);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RULER
|
* RULER
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Workaround specific to leaflet version
|
* Workaround specific to leaflet version
|
||||||
* See https://github.com/ljagis/leaflet-measure/issues/171 for details
|
* See https://github.com/ljagis/leaflet-measure/issues/171 for details
|
||||||
*/
|
*/
|
||||||
L.Control.Measure.include({
|
L.Control.Measure.include({
|
||||||
// set icon on the capture marker
|
// set icon on the capture marker
|
||||||
_setCaptureMarkerIcon: function () {
|
_setCaptureMarkerIcon: function () {
|
||||||
// disable autopan
|
// disable autopan
|
||||||
@@ -152,9 +152,9 @@ const { markers, players } = $world.get()
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const rulerOptions = {
|
const rulerOptions = {
|
||||||
position: 'topright',
|
position: 'topright',
|
||||||
primaryLengthUnit: 'kilometers',
|
primaryLengthUnit: 'kilometers',
|
||||||
secondaryLengthUnit: 'days',
|
secondaryLengthUnit: 'days',
|
||||||
@@ -170,19 +170,27 @@ const { markers, players } = $world.get()
|
|||||||
decimals: 1
|
decimals: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const measureControl = L.control.measure(rulerOptions);
|
const measureControl = L.control.measure(rulerOptions);
|
||||||
measureControl.addTo(map);
|
measureControl.addTo(map);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Flying to points of interests
|
||||||
|
*/
|
||||||
|
document.addEventListener('fly-to', (e) => {
|
||||||
|
const targetCoords = e.detail
|
||||||
|
map.flyTo([targetCoords.y, targetCoords.x], 5)
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
* DEBUGS
|
* DEBUGS
|
||||||
*/
|
*/
|
||||||
map.addEventListener('click', (event) => {
|
map.addEventListener('click', (event) => {
|
||||||
let lat = Math.round(event.latlng.lat * 100000) / 100000;
|
let lat = Math.round(event.latlng.lat * 100000) / 100000;
|
||||||
let lng = Math.round(event.latlng.lng * 100000) / 100000;
|
let lng = Math.round(event.latlng.lng * 100000) / 100000;
|
||||||
console.log(lat, lng);
|
console.log(lat, lng);
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { allTasks } from 'nanostores';
|
import { allTasks } from 'nanostores';
|
||||||
import { useStore } from '@nanostores/vue'
|
import { useStore } from '@nanostores/vue'
|
||||||
import { $world, flyTo } from '../worldStore';
|
import { $world } from '../worldStore';
|
||||||
|
|
||||||
import { useFocus, useMagicKeys, whenever } from '@vueuse/core';
|
import { useFocus, useMagicKeys, whenever } from '@vueuse/core';
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
@@ -9,7 +9,7 @@ import { computed, onMounted, ref } from 'vue';
|
|||||||
$world.listen(() => {})
|
$world.listen(() => {})
|
||||||
await allTasks()
|
await allTasks()
|
||||||
|
|
||||||
const { markers, players, lastCoords } = useStore($world).value
|
const { markers, players } = useStore($world).value
|
||||||
|
|
||||||
// Search functions
|
// Search functions
|
||||||
const qInput = ref()
|
const qInput = ref()
|
||||||
@@ -48,20 +48,24 @@ whenever(shortcutKeyCombo, () => {
|
|||||||
q.value = ""
|
q.value = ""
|
||||||
})
|
})
|
||||||
|
|
||||||
// Player geolocation
|
// Player geolocation (uses native JS to avoid changing leaflet library)
|
||||||
function handlePlayerTarget () {
|
onMounted(() => {
|
||||||
flyTo(players.markerCoords)
|
const playerBtnRef = document.querySelector('[data-to-players]')
|
||||||
}
|
const flyToPlayers = new CustomEvent('fly-to', { bubbles: true, detail: players.markerCoords })
|
||||||
|
|
||||||
|
playerBtnRef?.addEventListener('click', () => {
|
||||||
|
playerBtnRef.dispatchEvent(flyToPlayers)
|
||||||
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
{{ lastCoords }}
|
|
||||||
<div ref="searchBar" class="search-w" :data-focused="shouldBeActive">
|
<div ref="searchBar" class="search-w" :data-focused="shouldBeActive">
|
||||||
<div class="input-w">
|
<div class="input-w">
|
||||||
<i class="search-icon ph-fill ph-magnifying-glass"></i>
|
<i class="search-icon ph-fill ph-magnifying-glass"></i>
|
||||||
<input ref="qInput" type="text" v-model="q">
|
<input ref="qInput" type="text" v-model="q">
|
||||||
|
|
||||||
<button class="player-btn" @click.native="handlePlayerTarget">
|
<button data-to-players class="player-btn">
|
||||||
<i class="pin-icon ph-fill ph-map-pin"></i>
|
<i class="pin-icon ph-fill ph-map-pin"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,15 +4,6 @@ import type { MapCoords, MapMarker } from '../../types/Leaflet';
|
|||||||
export const $world = deepMap({
|
export const $world = deepMap({
|
||||||
markers: [] as MapMarker[],
|
markers: [] as MapMarker[],
|
||||||
players: {} as MapMarker,
|
players: {} as MapMarker,
|
||||||
lastCoords: {
|
|
||||||
y: 0,
|
|
||||||
x: 0,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export const flyTo = action($world, 'fly-to', (store, coords: MapCoords) => {
|
|
||||||
store.setKey('lastCoords', coords)
|
|
||||||
return store.get()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Fetch initial data
|
// Fetch initial data
|
||||||
|
|||||||
Reference in New Issue
Block a user