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
|
||||
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
|
||||
};
|
||||
import kaplay from "./modules/kaplay.js";
|
||||
|
||||
function moveBounds(polys, xoff = 0, yoff = xoff) {
|
||||
return polys.map(([x, y]) => [y + yoff, x + xoff]);
|
||||
}
|
||||
const map = document.querySelector("map");
|
||||
|
||||
// Get tile data
|
||||
class KaplayMap {
|
||||
kp;
|
||||
opts = {
|
||||
minZoomLevel: 0,
|
||||
maxZoomLevel: 3,
|
||||
};
|
||||
|
||||
async function getTileData(floor) {
|
||||
let tileDataReq = await fetch(`/data/${options.lang}/map/${floor}`);
|
||||
let tileData;
|
||||
if (tileDataReq.ok)
|
||||
tileData = await tileDataReq.json();
|
||||
#zoomLevel = 0;
|
||||
#scaleTween = null;
|
||||
|
||||
return tileData.map((room) =>
|
||||
L.polygon(moveBounds(room.poly, options.offs), defaultPolyStyling)
|
||||
);
|
||||
}
|
||||
startMousePos = null;
|
||||
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() {
|
||||
let floorDataReq = await fetch(`/data/${options.lang}/map/`);
|
||||
let floorData;
|
||||
if (floorDataReq.ok)
|
||||
floorData = await floorDataReq.json();
|
||||
// Zooming
|
||||
|
||||
// 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) => {
|
||||
const tileData = await getTileData(data.id);
|
||||
// Dragging
|
||||
|
||||
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([
|
||||
floorPoly,
|
||||
...tileData
|
||||
]);
|
||||
}));
|
||||
// ||| Completely unintelligible to the average person |||
|
||||
// vvv Sorry y'all xwx vvv
|
||||
|
||||
return {
|
||||
layers: floorData,
|
||||
floors: floors
|
||||
if (this.kp.isMousePressed()) {
|
||||
this.startMousePos = this.kp.mousePos();
|
||||
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 data = await getFloorData();
|
||||
const kaplaymap = new KaplayMap(kp, {});
|
||||
|
||||
let map = L.map('leaflet-map', {
|
||||
center: [0, 0],
|
||||
zoom: 2,
|
||||
crs: L.CRS.Simple
|
||||
});
|
||||
const bean = kp.loadBean();
|
||||
|
||||
const mainLayer = L.tileLayer('', {
|
||||
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();
|
||||
kp.add([kp.sprite("bean"), kp.pos(kp.center())]);
|
||||
|
|
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" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
||||
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
||||
crossorigin=""
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/assets/styles/styles.css"
|
||||
href="/assets/styles/style.css"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="leaflet-map"></div>
|
||||
<canvas id="map"></canvas>
|
||||
<script src="/assets/scripts/script.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -39,6 +39,37 @@ app.get('/data/:lang/events', async (req, res) => {
|
|||
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) => {
|
||||
// Get layers
|
||||
let layersReq = await fetch(new URL("map/layers.json", config.data_url));
|
||||
|
|
Loading…
Reference in a new issue