EventMapper/assets/scripts/KaplayMap/index.js

387 lines
9.9 KiB
JavaScript
Raw Normal View History

2024-06-01 18:36:00 +00:00
export class KaplayMap {
kp;
opts = {
2024-06-02 05:39:23 +00:00
minZoomLevel: 2,
maxZoomLevel: 5,
2024-06-01 18:36:00 +00:00
dblClickDuration: 0.2, // s
dblClickForgiveness: 30, // px
};
uiLayer;
2024-06-02 05:39:23 +00:00
#zoomLevel = this.opts.minZoomLevel;
2024-06-01 18:36:00 +00:00
#scaleTween = null;
startMousePos = null;
startCamPos = null;
startZoomLevel = null;
prevCamPos = null;
moveFriction = 0.25;
moveDead = 1;
#moveTween = null;
2024-06-02 05:39:23 +00:00
camBounds = null;
2024-06-01 18:36:00 +00:00
lastReleaseTime;
lastReleasePos;
lastPressTime;
lastPressPos;
mouseMode = "drag";
fingers = new Map([]);
startFingerDiff = null;
startFingerCenterPos = null;
startFingerZoomLevel = null;
constructor(kp, opts) {
this.kp = kp;
this.opts = {
...this.opts,
...opts,
};
2024-06-02 05:39:23 +00:00
this.zoomLevel_NoTween = this.opts.minZoomLevel;
this.kp.camPos(0);
2024-06-01 18:36:00 +00:00
this.kp.onScroll((delta) => {
const scrollDist = -delta.y / 120;
this.zoomTo(this.zoomLevel + scrollDist, this.kp.mousePos());
});
// Dragging
this.kp.onTouchStart((pos, touch) => {
this.fingers.set(touch.identifier, pos);
const fingerArr = Array.from(this.fingers.values());
this.startCamPos = this.kp.camPos();
this.startFingerCenterPos = fingerArr[0];
if (this.fingers.size > 1) {
this.clearMouseMode();
this.startFingerDiff = fingerArr[0].dist(fingerArr[1]);
const fingerDifference = fingerArr[0].sub(fingerArr[1]);
this.startFingerCenterPos = fingerArr[1].add(
fingerDifference.scale(0.5)
);
this.startFingerZoomLevel = this.zoomLevel;
this.mouseMode = "pinch";
} else if (this.fingers.size < 2) {
this.mouseMode = "drag";
}
});
this.kp.onTouchMove((pos, touch) => {
this.fingers.set(touch.identifier, pos);
});
this.kp.onTouchEnd((pos, touch) => {
if (this.fingers.size > 1) {
this.clearMouseMode();
}
this.fingers.delete(touch.identifier);
2024-06-02 05:39:23 +00:00
if (this.fingers.size < 1) {
this.checkBounds();
}
2024-06-01 18:36:00 +00:00
if (this.fingers.size < 2) {
const fingerArr = Array.from(this.fingers.values());
this.startFingerCenterPos = fingerArr[0];
this.startCamPos = this.kp.camPos();
this.startFingerDiff = null;
}
});
this.kp.onUpdate(() => {
const camScale = 1 / this.kp.camScale().y;
const fingerArr = Array.from(this.fingers.values());
if (this.fingers.size > 1) {
const curFingerDiff = fingerArr[0].dist(fingerArr[1]);
// Get centerpoint
const fingerDifference = fingerArr[0].sub(fingerArr[1]);
const fingerCenter = fingerArr[1].add(fingerDifference.scale(0.5));
this.zoomToNoTweenAbs(
this.startFingerZoomLevel +
Math.log2(curFingerDiff / this.startFingerDiff),
this.startCamPos.sub(
fingerCenter.sub(this.startFingerCenterPos).scale(camScale)
)
);
} else if (this.fingers.size == 1) {
if (this.mouseMode != "zoom") {
this.kp.camPos(
this.startCamPos.sub(
fingerArr[0].sub(this.startFingerCenterPos).scale(camScale)
)
);
}
}
});
this.kp.onUpdate(() => {
const curCamPos = this.kp.camPos();
const camScale = 1 / this.kp.camScale().y;
if (this.kp.isMousePressed()) {
// ignore if no double click
if (this.lastPressTime != null && this.lastPressPos != null) {
const clickDifference = this.kp.time() - this.lastPressTime;
const clickDistance = this.kp.mousePos().dist(this.lastPressPos);
// Double-click!
if (
clickDifference <= this.opts.dblClickDuration &&
clickDistance <= this.opts.dblClickForgiveness
) {
this.mouseMode = "zoom";
}
}
if (this.mouseMode == "drag") {
this.startMousePos = this.kp.mousePos();
this.startCamPos = this.kp.camPos();
} else if (this.mouseMode == "zoom") {
this.startMousePos = this.kp.mousePos();
this.startZoomLevel = this.zoomLevel;
}
// Set variables
this.lastPressTime = this.kp.time();
this.lastPressPos = this.kp.mousePos();
} else if (this.kp.isMouseReleased()) {
if (this.mouseMode == "pinch") return;
this.mouseMode = "drag";
// ignore if no double click
if (this.lastReleaseTime != null && this.lastReleasePos != null) {
const clickDifference = this.kp.time() - this.lastReleaseTime;
const clickDistance = this.kp.mousePos().dist(this.lastReleasePos);
// Double-click!
if (
clickDifference <= this.opts.dblClickDuration &&
clickDistance <= this.opts.dblClickForgiveness
) {
this.zoomTo(this.zoomLevel + 1, this.kp.mousePos());
}
}
// Set variables
this.lastReleaseTime = this.kp.time();
this.lastReleasePos = this.kp.mousePos();
2024-06-02 05:39:23 +00:00
// Check bounds
this.checkBounds();
2024-06-01 18:36:00 +00:00
}
if (this.kp.isMouseDown()) {
if (this.mouseMode == "drag") {
this.prevCamPos = this.kp.camPos();
this.kp.camPos(
this.startCamPos.sub(
this.kp.mousePos().sub(this.startMousePos).scale(camScale)
)
);
} else if (this.mouseMode == "zoom") {
this.zoomToNoTween(
this.startZoomLevel +
-this.kp.mousePos().sub(this.startMousePos).y /
(this.kp.height() / 2),
this.startMousePos
);
}
}
});
}
initGrid() {
const grid = this.kp.loadSprite(null, "/assets/images/grid.png");
this.kp.onDraw(() => {
this.kp.drawSprite({
sprite: grid,
tiled: true,
opacity: 0.25,
2024-06-02 05:39:23 +00:00
width: this.kp.width() + 200,
height: this.kp.height() + 200,
anchor: "center",
2024-06-01 18:36:00 +00:00
pos: this.kp.vec2(
2024-06-02 05:39:23 +00:00
Math.floor(this.kp.camPos().x / 100) * 100 + 50.5,
Math.floor(this.kp.camPos().y / 100) * 100 + 0.5
2024-06-01 18:36:00 +00:00
),
});
});
return grid;
}
clearMouseMode() {
this.lastReleaseTime = null;
this.lastReleasePos = null;
this.lastPressTime = null;
this.lastPressPos = null;
this.prevCamPos = null;
}
2024-06-02 05:39:23 +00:00
checkBounds() {
const camPos = this.kp.camPos();
if (this.camBounds == null) return;
if (this.kp.testRectPoint(this.camBounds, camPos)) return;
const boundsCenter = this.camBounds.center();
const cast = this.camBounds.raycast(
camPos,
this.kp.Vec2.fromAngle(camPos.angle(boundsCenter) + 180).scale(40000)
);
if (cast == null) return;
if (this.#moveTween != null) {
this.#moveTween.finish();
}
this.#moveTween = this.kp.tween(
camPos,
cast.point,
0.25,
this.kp.camPos,
this.kp.easings.easeOutQuad
);
this.#moveTween.then(() => {
this.#moveTween = null;
});
}
2024-06-01 18:36:00 +00:00
get zoomLevelLimit() {
if (this.#zoomLevel == this.opts.minZoomLevel) return -1;
if (this.#zoomLevel == this.opts.maxZoomLevel) return 1;
return 0;
}
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;
});
}
set zoomLevel_NoTween(newZoom) {
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;
this.kp.camScale(linearToLog);
}
zoomTo(newZoom, position) {
const curCamPos = this.kp.camPos();
const camScl = 1 / this.kp.camScale().y;
const diff = this.kp.center().sub(position).scale(camScl);
this.zoomLevel = newZoom;
let newZoomLog = 1 / Math.pow(2, newZoom);
const newDiff = this.kp.center().sub(position).scale(newZoomLog);
2024-06-02 05:39:23 +00:00
if (newZoom < this.opts.minZoomLevel) return;
if (newZoom > this.opts.maxZoomLevel) return;
2024-06-01 18:36:00 +00:00
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;
2024-06-02 05:39:23 +00:00
this.checkBounds();
});
}
zoomToAbs(newZoom, position) {
const curCamPos = this.kp.camPos();
this.zoomLevel = newZoom;
if (this.#moveTween != null) {
this.#moveTween.finish();
}
this.#moveTween = this.kp.tween(
curCamPos,
position,
0.25,
this.kp.camPos,
this.kp.easings.easeOutQuad
);
this.#moveTween.then(() => {
this.#moveTween = null;
this.checkBounds();
2024-06-01 18:36:00 +00:00
});
}
zoomToNoTween(newZoom, position) {
const curCamPos = this.kp.camPos();
const camScl = 1 / this.kp.camScale().y;
const diff = this.kp.center().sub(position).scale(camScl);
this.zoomLevel_NoTween = newZoom;
let newZoomLog = 1 / Math.pow(2, newZoom);
const newDiff = this.kp.center().sub(position).scale(newZoomLog);
2024-06-02 05:39:23 +00:00
if (newZoom < this.opts.minZoomLevel) return;
if (newZoom > this.opts.maxZoomLevel) return;
2024-06-01 18:36:00 +00:00
this.kp.camPos(curCamPos.sub(diff).add(newDiff));
2024-06-02 05:39:23 +00:00
this.checkBounds();
2024-06-01 18:36:00 +00:00
}
zoomToNoTweenAbs(newZoom, position) {
this.zoomLevel_NoTween = newZoom;
this.kp.camPos(position);
2024-06-02 05:39:23 +00:00
this.checkBounds();
2024-06-01 18:36:00 +00:00
}
}