Move to Kaplay
This commit is contained in:
parent
5b2aa4dc8b
commit
30ac08e50f
7 changed files with 7184 additions and 127 deletions
File diff suppressed because one or more lines are too long
7001
assets/scripts/modules/kaplay.js
Normal file
7001
assets/scripts/modules/kaplay.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,114 +1,160 @@
|
||||||
import "./leaflet.js"; // window.L
|
import kaplay from "./modules/kaplay.js";
|
||||||
const options = {
|
|
||||||
lang: "en_US",
|
|
||||||
offs: 0,
|
|
||||||
viewportPadding: 20,
|
|
||||||
boundsPadding: 0.1,
|
|
||||||
}
|
|
||||||
const defaultPolyStyling = {
|
|
||||||
color: "#000000",
|
|
||||||
opacity: 1,
|
|
||||||
fill: true,
|
|
||||||
fillOpacity: 0.25,
|
|
||||||
weight: 3
|
|
||||||
};
|
|
||||||
|
|
||||||
function moveBounds(polys, xoff = 0, yoff = xoff) {
|
const map = document.querySelector("map");
|
||||||
return polys.map(([x, y]) => [y + yoff, x + xoff]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get tile data
|
class KaplayMap {
|
||||||
|
kp;
|
||||||
|
opts = {
|
||||||
|
minZoomLevel: 0,
|
||||||
|
maxZoomLevel: 3,
|
||||||
|
};
|
||||||
|
|
||||||
async function getTileData(floor) {
|
#zoomLevel = 0;
|
||||||
let tileDataReq = await fetch(`/data/${options.lang}/map/${floor}`);
|
#scaleTween = null;
|
||||||
let tileData;
|
|
||||||
if (tileDataReq.ok)
|
|
||||||
tileData = await tileDataReq.json();
|
|
||||||
|
|
||||||
return tileData.map((room) =>
|
startMousePos = null;
|
||||||
L.polygon(moveBounds(room.poly, options.offs), defaultPolyStyling)
|
startCamPos = null;
|
||||||
);
|
prevCamPos = null;
|
||||||
}
|
moveVelocity = null;
|
||||||
|
moveFriction = 0.25;
|
||||||
|
moveDead = 1;
|
||||||
|
#moveTween = null;
|
||||||
|
|
||||||
// Get floor data
|
constructor(kp, opts) {
|
||||||
|
this.kp = kp;
|
||||||
|
this.opts = {
|
||||||
|
...this.opts,
|
||||||
|
...opts,
|
||||||
|
};
|
||||||
|
|
||||||
async function getFloorData() {
|
// Zooming
|
||||||
let floorDataReq = await fetch(`/data/${options.lang}/map/`);
|
|
||||||
let floorData;
|
|
||||||
if (floorDataReq.ok)
|
|
||||||
floorData = await floorDataReq.json();
|
|
||||||
|
|
||||||
// Add to Leaflet
|
this.kp.onScroll((delta) => {
|
||||||
|
const scrollDist = -delta.y / 120;
|
||||||
|
this.zoomTo(this.zoomLevel + scrollDist, this.kp.mousePos());
|
||||||
|
});
|
||||||
|
|
||||||
const floors = await Promise.all(floorData.map(async (data) => {
|
// Dragging
|
||||||
const tileData = await getTileData(data.id);
|
|
||||||
|
|
||||||
const floorPoly = L.polygon(moveBounds(data.poly, options.offs), defaultPolyStyling)
|
this.kp.onUpdate(() => {
|
||||||
|
const curCamPos = this.kp.camPos();
|
||||||
|
const camScale = 1 / this.kp.camScale().y;
|
||||||
|
|
||||||
return L.featureGroup([
|
// ||| Completely unintelligible to the average person |||
|
||||||
floorPoly,
|
// vvv Sorry y'all xwx vvv
|
||||||
...tileData
|
|
||||||
]);
|
|
||||||
}));
|
|
||||||
|
|
||||||
return {
|
if (this.kp.isMousePressed()) {
|
||||||
layers: floorData,
|
this.startMousePos = this.kp.mousePos();
|
||||||
floors: floors
|
this.startCamPos = this.kp.camPos();
|
||||||
|
} else if (this.kp.isMouseReleased() && this.prevCamPos != null) {
|
||||||
|
this.moveVelocity = this.prevCamPos.sub(curCamPos).scale(1 / camScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.kp.isMouseDown()) {
|
||||||
|
this.prevCamPos = this.kp.camPos();
|
||||||
|
this.kp.camPos(
|
||||||
|
this.startCamPos.sub(
|
||||||
|
this.kp.mousePos().sub(this.startMousePos).scale(camScale)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else if (this.moveVelocity?.x != 0 && this.moveVelocity?.y != 0)
|
||||||
|
this.kp.camPos(curCamPos.sub(this.moveVelocity?.scale(camScale) ?? 0));
|
||||||
|
|
||||||
|
// ^^^ Completely unintelligible to the average person ^^^
|
||||||
|
// ||| Sorry y'all xwx |||
|
||||||
|
|
||||||
|
if (this.moveVelocity == null) return;
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.moveVelocity.x <= this.moveDead &&
|
||||||
|
this.moveVelocity.x >= -this.moveDead
|
||||||
|
)
|
||||||
|
this.moveVelocity.x = 0;
|
||||||
|
if (
|
||||||
|
this.moveVelocity.y <= this.moveDead &&
|
||||||
|
this.moveVelocity.y >= -this.moveDead
|
||||||
|
)
|
||||||
|
this.moveVelocity.y = 0;
|
||||||
|
|
||||||
|
this.moveVelocity = this.moveVelocity.scale(1 - this.moveFriction);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get zoomLevel() {
|
||||||
|
return this.#zoomLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
set zoomLevel(newZoom) {
|
||||||
|
const cameraZoom = this.kp.camScale().y;
|
||||||
|
|
||||||
|
let addLinear = newZoom;
|
||||||
|
|
||||||
|
if (addLinear < this.opts.minZoomLevel) addLinear = this.opts.minZoomLevel;
|
||||||
|
if (addLinear > this.opts.maxZoomLevel) addLinear = this.opts.maxZoomLevel;
|
||||||
|
|
||||||
|
let linearToLog = Math.pow(2, addLinear);
|
||||||
|
this.#zoomLevel = addLinear;
|
||||||
|
|
||||||
|
if (this.#scaleTween != null) {
|
||||||
|
this.#scaleTween.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#scaleTween = this.kp.tween(
|
||||||
|
cameraZoom,
|
||||||
|
linearToLog,
|
||||||
|
0.25,
|
||||||
|
this.kp.camScale,
|
||||||
|
this.kp.easings.easeOutQuad
|
||||||
|
);
|
||||||
|
this.#scaleTween.then(() => {
|
||||||
|
this.#scaleTween = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
zoomTo(newZoom, position) {
|
||||||
|
const curCamPos = this.kp.camPos();
|
||||||
|
const camScale = 1 / this.kp.camScale().y;
|
||||||
|
|
||||||
|
const diff = this.kp.center().sub(position).scale(camScale);
|
||||||
|
|
||||||
|
this.zoomLevel = newZoom;
|
||||||
|
let newZoomLog = 1 / Math.pow(2, newZoom);
|
||||||
|
|
||||||
|
const newDiff = this.kp.center().sub(position).scale(newZoomLog);
|
||||||
|
|
||||||
|
if (newZoom < this.opts.minZoomLevel) return;
|
||||||
|
if (newZoom > this.opts.maxZoomLevel) return;
|
||||||
|
|
||||||
|
if (this.#moveTween != null) {
|
||||||
|
this.#moveTween.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#moveTween = this.kp.tween(
|
||||||
|
curCamPos,
|
||||||
|
curCamPos.sub(diff).add(newDiff),
|
||||||
|
0.25,
|
||||||
|
this.kp.camPos,
|
||||||
|
this.kp.easings.easeOutQuad
|
||||||
|
);
|
||||||
|
this.#moveTween.then(() => {
|
||||||
|
this.#moveTween = null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create map
|
const kp = kaplay({
|
||||||
|
canvas: map,
|
||||||
|
focus: true,
|
||||||
|
loadingScreen: false,
|
||||||
|
crisp: false,
|
||||||
|
debug: false,
|
||||||
|
global: false,
|
||||||
|
maxFPS: 120,
|
||||||
|
background: "404040",
|
||||||
|
});
|
||||||
|
|
||||||
async function createMap() {
|
const kaplaymap = new KaplayMap(kp, {});
|
||||||
const data = await getFloorData();
|
|
||||||
|
|
||||||
let map = L.map('leaflet-map', {
|
const bean = kp.loadBean();
|
||||||
center: [0, 0],
|
|
||||||
zoom: 2,
|
|
||||||
crs: L.CRS.Simple
|
|
||||||
});
|
|
||||||
|
|
||||||
const mainLayer = L.tileLayer('', {
|
kp.add([kp.sprite("bean"), kp.pos(kp.center())]);
|
||||||
maxZoom: 4,
|
|
||||||
minZoom: 1,
|
|
||||||
attribution: 'Test data'
|
|
||||||
}).addTo(map);
|
|
||||||
|
|
||||||
const floor = data.floors[0].addTo(map);
|
|
||||||
const layerBounds = floor.getBounds();
|
|
||||||
|
|
||||||
const blb = layerBounds.pad(options.boundsPadding);
|
|
||||||
|
|
||||||
map.setMaxBounds(blb);
|
|
||||||
|
|
||||||
map.fitBounds(layerBounds, { padding: [options.viewportPadding, options.viewportPadding] });
|
|
||||||
|
|
||||||
const entries = Object.fromEntries(data.floors.map((floor, i) => [
|
|
||||||
`${data.layers[i].name}`,
|
|
||||||
floor
|
|
||||||
]));
|
|
||||||
|
|
||||||
let layerControl = L.control.layers(entries).addTo(map);
|
|
||||||
|
|
||||||
return {
|
|
||||||
floors: data.floors,
|
|
||||||
map,
|
|
||||||
mainLayer
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function mainFunction() {
|
|
||||||
const { floors, map, mainLayer } = await createMap();
|
|
||||||
|
|
||||||
map.on('baselayerchange', ({ layer, name }) => {
|
|
||||||
const layerBounds = layer.getBounds();
|
|
||||||
|
|
||||||
const blb = layerBounds.pad(options.boundsPadding);
|
|
||||||
|
|
||||||
map.setMaxBounds(blb);
|
|
||||||
|
|
||||||
map.fitBounds(layerBounds, { padding: [options.viewportPadding, options.viewportPadding] });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
mainFunction();
|
|
||||||
|
|
7
assets/styles/style.css
Normal file
7
assets/styles/style.css
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
body, html {
|
||||||
|
margin: 0;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
|
@ -1,16 +0,0 @@
|
||||||
body, html {
|
|
||||||
margin: 0;
|
|
||||||
height: 100vh;
|
|
||||||
width: 100vw;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#leaflet-map {
|
|
||||||
background-color: #404040;
|
|
||||||
display: inline-block;
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,17 +8,11 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
href="/assets/styles/style.css"
|
||||||
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
|
||||||
crossorigin=""
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="/assets/styles/styles.css"
|
|
||||||
/>
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="leaflet-map"></div>
|
<canvas id="map"></canvas>
|
||||||
<script src="/assets/scripts/script.js" type="module"></script>
|
<script src="/assets/scripts/script.js" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -39,6 +39,37 @@ app.get('/data/:lang/events', async (req, res) => {
|
||||||
return res.send(merged);
|
return res.send(merged);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.get('/data/:lang/events/:layer', async (req, res) => {
|
||||||
|
// Get events
|
||||||
|
let eventsReq = await fetch(new URL("events/events.json", config.data_url));
|
||||||
|
let events;
|
||||||
|
if (eventsReq.ok)
|
||||||
|
events = await eventsReq.json();
|
||||||
|
else
|
||||||
|
return res.status(400).send("Bad Request");
|
||||||
|
|
||||||
|
// Get localization
|
||||||
|
let l10nReq = await fetch(new URL(`localization/${req.params.lang}.json`, config.data_url));
|
||||||
|
let l10n;
|
||||||
|
if (l10nReq.ok)
|
||||||
|
l10n = await l10nReq.json();
|
||||||
|
else
|
||||||
|
return res.status(404).send("Localization not found!");
|
||||||
|
|
||||||
|
const clippedEvents = events.filter((event) => event.where.layer == req.params.layer);
|
||||||
|
|
||||||
|
// Merge events and localization
|
||||||
|
const merged = events.map(event => {
|
||||||
|
const localizationKey = l10n.__events[event.id];
|
||||||
|
return {
|
||||||
|
...event,
|
||||||
|
...localizationKey
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.send(merged);
|
||||||
|
})
|
||||||
|
|
||||||
app.get('/data/:lang/map', async (req, res) => {
|
app.get('/data/:lang/map', async (req, res) => {
|
||||||
// Get layers
|
// Get layers
|
||||||
let layersReq = await fetch(new URL("map/layers.json", config.data_url));
|
let layersReq = await fetch(new URL("map/layers.json", config.data_url));
|
||||||
|
|
Loading…
Reference in a new issue