288 lines
8.6 KiB
JavaScript
288 lines
8.6 KiB
JavaScript
|
|
const canvas = document.getElementById("mapCanvas");
|
||
|
|
const fogCanvas = document.createElement("canvas");
|
||
|
|
const ctx = canvas.getContext("2d");
|
||
|
|
const fogCtx = fogCanvas.getContext("2d");
|
||
|
|
const iframe = document.getElementById("worldmap-iframe");
|
||
|
|
|
||
|
|
let image = null;
|
||
|
|
let offsetX = 0, offsetY = 0;
|
||
|
|
let scale = 1;
|
||
|
|
let isDragging = false;
|
||
|
|
let startX, startY;
|
||
|
|
let fogEnabled = false;
|
||
|
|
let fogMode = "erase";
|
||
|
|
let brushSize = 50;
|
||
|
|
let mouseX = 0, mouseY = 0;
|
||
|
|
|
||
|
|
const channel = new BroadcastChannel("mapsync");
|
||
|
|
let MapX = 0;
|
||
|
|
let MapY = 0;
|
||
|
|
let MapZoom = 2;
|
||
|
|
|
||
|
|
function resizeCanvas() {
|
||
|
|
canvas.width = window.innerWidth;
|
||
|
|
canvas.height = window.innerHeight;
|
||
|
|
draw();
|
||
|
|
if (fogEnabled) drawBrushPreview(mouseX, mouseY);
|
||
|
|
}
|
||
|
|
window.addEventListener("resize", resizeCanvas);
|
||
|
|
resizeCanvas();
|
||
|
|
|
||
|
|
// === Fog Controls ===
|
||
|
|
document.getElementById("toggleFog").addEventListener("change", (e) => {
|
||
|
|
fogEnabled = e.target.checked;
|
||
|
|
channel.postMessage({ type: "fogEnabled", enabled: fogEnabled });
|
||
|
|
draw();
|
||
|
|
if (fogEnabled) drawBrushPreview(mouseX, mouseY);
|
||
|
|
});
|
||
|
|
|
||
|
|
document.getElementById("fogMode").addEventListener("click", () => {
|
||
|
|
fogMode = fogMode === "erase" ? "paint" : "erase";
|
||
|
|
document.getElementById("fogMode").innerText = fogMode === "erase" ? "Modus: Radierer" : "Modus: Nebel";
|
||
|
|
});
|
||
|
|
|
||
|
|
document.getElementById("fogClear").addEventListener("click", () => {
|
||
|
|
const btn = document.getElementById("fogClear");
|
||
|
|
btn.disabled = true;
|
||
|
|
setTimeout(() => btn.disabled = false, 1000);
|
||
|
|
fogCtx.clearRect(0, 0, fogCanvas.width, fogCanvas.height);
|
||
|
|
syncFog(); draw();
|
||
|
|
if (fogEnabled) drawBrushPreview(mouseX, mouseY);
|
||
|
|
});
|
||
|
|
|
||
|
|
document.getElementById("fogFull").addEventListener("click", () => {
|
||
|
|
const btn = document.getElementById("fogFull");
|
||
|
|
btn.disabled = true;
|
||
|
|
setTimeout(() => btn.disabled = false, 1000);
|
||
|
|
fogCtx.fillStyle = "rgba(50, 50, 50, 1)";
|
||
|
|
fogCtx.fillRect(0, 0, fogCanvas.width, fogCanvas.height);
|
||
|
|
syncFog(); draw();
|
||
|
|
if (fogEnabled) drawBrushPreview(mouseX, mouseY);
|
||
|
|
});
|
||
|
|
|
||
|
|
document.getElementById("brushSize").addEventListener("input", (e) => {
|
||
|
|
brushSize = 50 * parseInt(e.target.value);
|
||
|
|
});
|
||
|
|
|
||
|
|
// === Mouse Interaktion ===
|
||
|
|
canvas.addEventListener("dragover", e => e.preventDefault());
|
||
|
|
canvas.addEventListener("drop", e => {
|
||
|
|
e.preventDefault();
|
||
|
|
const file = e.dataTransfer.files[0];
|
||
|
|
const reader = new FileReader();
|
||
|
|
reader.onload = evt => {
|
||
|
|
const img = new Image();
|
||
|
|
img.onload = () => {
|
||
|
|
image = img;
|
||
|
|
//Fog auf Größe des Geladenen Bildes anpassen.
|
||
|
|
fogCanvas.width = img.width;
|
||
|
|
fogCanvas.height = img.height;
|
||
|
|
fogCtx.clearRect(0, 0, fogCanvas.width, fogCanvas.height);
|
||
|
|
draw();
|
||
|
|
if (fogEnabled) drawBrushPreview(mouseX, mouseY);
|
||
|
|
channel.postMessage({ type: "mapData", data: evt.target.result });
|
||
|
|
channel.postMessage({ type: "fogEnabled", enabled: fogEnabled });
|
||
|
|
syncFog();
|
||
|
|
};
|
||
|
|
img.src = evt.target.result;
|
||
|
|
};
|
||
|
|
reader.readAsDataURL(file);
|
||
|
|
});
|
||
|
|
|
||
|
|
function LoadImageMap(i) {
|
||
|
|
const img = new Image();
|
||
|
|
img.onload = () => {
|
||
|
|
image = img;
|
||
|
|
fogCanvas.width = img.width;
|
||
|
|
fogCanvas.height = img.height;
|
||
|
|
fogCtx.clearRect(0, 0, fogCanvas.width, fogCanvas.height);
|
||
|
|
draw();
|
||
|
|
if (fogEnabled) drawBrushPreview(mouseX, mouseY);
|
||
|
|
channel.postMessage({ type: "mapData", data: i });
|
||
|
|
channel.postMessage({ type: "fogEnabled", enabled: fogEnabled });
|
||
|
|
syncFog();
|
||
|
|
};
|
||
|
|
img.src = i;
|
||
|
|
}
|
||
|
|
|
||
|
|
window.LoadImageMap = LoadImageMap;
|
||
|
|
|
||
|
|
canvas.addEventListener("mousedown", e => {
|
||
|
|
if (e.button === 1) {
|
||
|
|
isDragging = true;
|
||
|
|
startX = e.clientX - offsetX;
|
||
|
|
startY = e.clientY - offsetY;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
canvas.addEventListener("mouseup", () => { isDragging = false; });
|
||
|
|
canvas.addEventListener("mouseout", () => { isDragging = false; });
|
||
|
|
canvas.addEventListener("mousemove", e => {
|
||
|
|
mouseX = e.clientX;
|
||
|
|
mouseY = e.clientY;
|
||
|
|
if (fogEnabled) draw();
|
||
|
|
if (isDragging) {
|
||
|
|
offsetX = e.clientX - startX;
|
||
|
|
offsetY = e.clientY - startY;
|
||
|
|
syncView();
|
||
|
|
draw();
|
||
|
|
if (fogEnabled) drawBrushPreview(mouseX, mouseY);
|
||
|
|
} else if (fogEnabled && e.buttons === 1) {
|
||
|
|
const x = (mouseX - offsetX) / scale;
|
||
|
|
const y = (mouseY - offsetY) / scale;
|
||
|
|
fogCtx.globalCompositeOperation = fogMode === "erase" ? "destination-out" : "source-over";
|
||
|
|
fogCtx.fillStyle = "rgba(50, 50, 50, 0.9)";
|
||
|
|
fogCtx.beginPath();
|
||
|
|
fogCtx.arc(x, y, brushSize, 0, 2 * Math.PI);
|
||
|
|
fogCtx.fill();
|
||
|
|
syncFog(); draw();
|
||
|
|
drawBrushPreview(mouseX, mouseY);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
canvas.addEventListener("wheel", e => {
|
||
|
|
e.preventDefault();
|
||
|
|
const worldX = (e.clientX - offsetX) / scale;
|
||
|
|
const worldY = (e.clientY - offsetY) / scale;
|
||
|
|
const zoomFactor = e.deltaY < 0 ? 1.1 : 0.9;
|
||
|
|
scale *= zoomFactor;
|
||
|
|
offsetX = e.clientX - worldX * scale;
|
||
|
|
offsetY = e.clientY - worldY * scale;
|
||
|
|
syncView(); draw();
|
||
|
|
if (fogEnabled) drawBrushPreview(mouseX, mouseY);
|
||
|
|
});
|
||
|
|
|
||
|
|
function syncView() {
|
||
|
|
channel.postMessage({ type: "viewData", offsetX, offsetY, scale });
|
||
|
|
}
|
||
|
|
|
||
|
|
function syncFog() {
|
||
|
|
const fogURL = fogCanvas.toDataURL("image/png");
|
||
|
|
channel.postMessage({ type: "fogData", data: fogURL });
|
||
|
|
}
|
||
|
|
|
||
|
|
function draw() {
|
||
|
|
ctx.setTransform(scale, 0, 0, scale, offsetX, offsetY);
|
||
|
|
ctx.clearRect(-offsetX / scale, -offsetY / scale, canvas.width / scale, canvas.height / scale);
|
||
|
|
if (image) ctx.drawImage(image, 0, 0);
|
||
|
|
if (fogEnabled && fogCanvas.width && fogCanvas.height) {
|
||
|
|
ctx.globalAlpha = 0.4;
|
||
|
|
ctx.drawImage(fogCanvas, 0, 0);
|
||
|
|
ctx.globalAlpha = 1;
|
||
|
|
}
|
||
|
|
if (fogEnabled) drawBrushPreview(mouseX, mouseY);
|
||
|
|
}
|
||
|
|
|
||
|
|
function drawBrushPreview(mouseX, mouseY) {
|
||
|
|
const worldX = (mouseX - offsetX) / scale;
|
||
|
|
const worldY = (mouseY - offsetY) / scale;
|
||
|
|
ctx.save();
|
||
|
|
ctx.setTransform(scale, 0, 0, scale, offsetX, offsetY);
|
||
|
|
ctx.strokeStyle = "rgba(255,255,255,0.8)";
|
||
|
|
ctx.lineWidth = 2 / scale;
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.arc(worldX, worldY, brushSize, 0, 2 * Math.PI);
|
||
|
|
ctx.stroke();
|
||
|
|
ctx.restore();
|
||
|
|
}
|
||
|
|
|
||
|
|
window.addEventListener("keydown", (e) => {
|
||
|
|
if (e.key.toLowerCase() === "f") {
|
||
|
|
fogEnabled = !fogEnabled;
|
||
|
|
document.getElementById("toggleFog").checked = fogEnabled;
|
||
|
|
channel.postMessage({ type: "fogEnabled", enabled: fogEnabled });
|
||
|
|
draw();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
document.getElementById("toggleHexgrid").addEventListener("change", (e) => {
|
||
|
|
channel.postMessage({ type: "hexgrid", enabled: e.target.checked });
|
||
|
|
});
|
||
|
|
|
||
|
|
document.getElementById("gridScaleSlider").addEventListener("input", (e) => {
|
||
|
|
const scale = parseFloat(e.target.value);
|
||
|
|
channel.postMessage({ type: "hexgridScale", scale });
|
||
|
|
});
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
// === Weltkarte Funktionen ===
|
||
|
|
let worldMapMode = false;
|
||
|
|
let previousImageSrc = null;
|
||
|
|
|
||
|
|
// === Leaflet View Update Intervall ===
|
||
|
|
setInterval(() => {
|
||
|
|
const iframe = document.getElementById("worldmap-iframe");
|
||
|
|
if (iframe?.contentWindow && worldMapMode) {
|
||
|
|
iframe.contentWindow.postMessage("getView", "*");
|
||
|
|
}
|
||
|
|
}, 500);
|
||
|
|
|
||
|
|
window.addEventListener("message", (event) => {
|
||
|
|
const data = event.data;
|
||
|
|
if (data?.type === "viewData") {
|
||
|
|
MapX = data.center.lng;
|
||
|
|
MapY = data.center.lat;
|
||
|
|
MapZoom = data.zoom;
|
||
|
|
channel.postMessage({ type: "MapCoord", MapX, MapY, MapZoom}); //Dem Slave die Karten Werte schicken.
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
const mapButton = document.getElementById("mapToggleButton");
|
||
|
|
if (mapButton) {
|
||
|
|
mapButton.addEventListener("click", () => {
|
||
|
|
if (!worldMapMode) {
|
||
|
|
previousImageSrc = image?.src || "pause.png";
|
||
|
|
loadMap();
|
||
|
|
channel.postMessage({ type: "mapMode", enabled: true });
|
||
|
|
mapButton.textContent = "↩️";
|
||
|
|
worldMapMode = true;
|
||
|
|
syncWorldMapView();
|
||
|
|
} else {
|
||
|
|
restoreImage(previousImageSrc);
|
||
|
|
channel.postMessage({ type: "mapMode", enabled: false });
|
||
|
|
mapButton.textContent = "🗺";
|
||
|
|
worldMapMode = false;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
//Laden der iframe WorldMap
|
||
|
|
function loadMap() {
|
||
|
|
const iframe = document.getElementById("worldmap-iframe");
|
||
|
|
if (!iframe) return;
|
||
|
|
iframe.style.display = "block";
|
||
|
|
if (iframe.contentWindow) {
|
||
|
|
iframe.contentWindow.postMessage({ action: "syncRequest" }, "*");
|
||
|
|
} else {
|
||
|
|
iframe.onload = () => {
|
||
|
|
iframe.contentWindow.postMessage({ action: "syncRequest" }, "*");
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//Nach dem schließen der iframe WorldMap, wieder die letzte Map herstellen.
|
||
|
|
function restoreImage(src) {
|
||
|
|
const iframe = document.getElementById("worldmap-iframe");
|
||
|
|
const canvas = document.getElementById("mapCanvas");
|
||
|
|
|
||
|
|
if (iframe) iframe.style.display = "none";
|
||
|
|
if (canvas) canvas.style.display = "block";
|
||
|
|
if (!src) return;
|
||
|
|
|
||
|
|
const img = new Image();
|
||
|
|
img.onload = () => {
|
||
|
|
image = img;
|
||
|
|
fogCanvas.width = img.width;
|
||
|
|
fogCanvas.height = img.height;
|
||
|
|
fogCtx.clearRect(0, 0, fogCanvas.width, fogCanvas.height);
|
||
|
|
draw();
|
||
|
|
};
|
||
|
|
img.src = src;
|
||
|
|
}
|
||
|
|
|
||
|
|
function syncWorldMapView() {
|
||
|
|
const iframe = document.getElementById("worldmap-iframe");
|
||
|
|
if (worldMapMode && iframe?.contentWindow) {
|
||
|
|
iframe.contentWindow.postMessage("getView", "*");
|
||
|
|
}
|
||
|
|
}
|