dawarich/app/javascript/maps/fog_of_war.js

128 lines
3.4 KiB
JavaScript
Raw Normal View History

2025-01-14 17:29:48 -05:00
export function initializeFogCanvas(map) {
// Remove existing fog canvas if it exists
const oldFog = document.getElementById('fog');
if (oldFog) oldFog.remove();
// Create new fog canvas
const fog = document.createElement('canvas');
fog.id = 'fog';
fog.style.position = 'absolute';
fog.style.top = '0';
fog.style.left = '0';
fog.style.pointerEvents = 'none';
fog.style.zIndex = '400';
// Set canvas size to match map container
const mapSize = map.getSize();
fog.width = mapSize.x;
fog.height = mapSize.y;
// Add canvas to map container
map.getContainer().appendChild(fog);
return fog;
}
2025-05-14 15:04:47 -04:00
export function drawFogCanvas(map, markers, clearFogRadius, fogLinethreshold) {
2025-01-14 17:29:48 -05:00
const fog = document.getElementById('fog');
2025-01-20 05:41:45 -05:00
// Return early if fog element doesn't exist or isn't a canvas
if (!fog || !(fog instanceof HTMLCanvasElement)) return;
2025-01-14 17:29:48 -05:00
const ctx = fog.getContext('2d');
if (!ctx) return;
const size = map.getSize();
2025-05-14 12:56:30 -04:00
// 1) Paint base fog
2025-01-14 17:29:48 -05:00
ctx.clearRect(0, 0, size.x, size.y);
ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';
ctx.fillRect(0, 0, size.x, size.y);
2025-05-14 12:56:30 -04:00
// 2) Cut out holes
2025-01-14 17:29:48 -05:00
ctx.globalCompositeOperation = 'destination-out';
2025-05-14 12:56:30 -04:00
// 3) Build & sort points
const pts = markers
.map(pt => {
const pixel = map.latLngToContainerPoint(L.latLng(pt[0], pt[1]));
return { pixel, time: parseInt(pt[4], 10) };
})
.sort((a, b) => a.time - b.time);
const radiusPx = Math.max(metersToPixels(map, clearFogRadius), 2);
console.log(radiusPx);
// 4) Mark which pts are part of a line
const connected = new Array(pts.length).fill(false);
for (let i = 0; i < pts.length - 1; i++) {
2025-05-14 15:04:47 -04:00
if (pts[i + 1].time - pts[i].time <= fogLinethreshold) {
2025-05-14 12:56:30 -04:00
connected[i] = true;
connected[i + 1] = true;
}
}
// 5) Draw circles only for “alone” points
pts.forEach((pt, i) => {
if (!connected[i]) {
ctx.fillStyle = 'rgba(255,255,255,1)';
ctx.beginPath();
ctx.arc(pt.pixel.x, pt.pixel.y, radiusPx, 0, Math.PI * 2);
ctx.fill();
}
2025-01-14 17:29:48 -05:00
});
2025-05-14 12:56:30 -04:00
// 6) Draw rounded lines
ctx.lineWidth = radiusPx * 2;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.strokeStyle = 'rgba(255,255,255,1)';
for (let i = 0; i < pts.length - 1; i++) {
2025-05-14 15:04:47 -04:00
if (pts[i + 1].time - pts[i].time <= fogLinethreshold) {
2025-05-14 12:56:30 -04:00
ctx.beginPath();
ctx.moveTo(pts[i].pixel.x, pts[i].pixel.y);
ctx.lineTo(pts[i + 1].pixel.x, pts[i + 1].pixel.y);
ctx.stroke();
}
}
// 7) Reset composite operation
2025-01-14 17:29:48 -05:00
ctx.globalCompositeOperation = 'source-over';
}
function metersToPixels(map, meters) {
const zoom = map.getZoom();
const latLng = map.getCenter();
const metersPerPixel = getMetersPerPixel(latLng.lat, zoom);
return meters / metersPerPixel;
}
function getMetersPerPixel(latitude, zoom) {
const earthCircumference = 40075016.686;
return earthCircumference * Math.cos(latitude * Math.PI / 180) / Math.pow(2, zoom + 8);
}
export function createFogOverlay() {
return L.Layer.extend({
onAdd: (map) => {
initializeFogCanvas(map);
2025-01-20 05:41:45 -05:00
// Add resize event handlers to update fog size
map.on('resize', () => {
// Set canvas size to match map container
const mapSize = map.getSize();
fog.width = mapSize.x;
fog.height = mapSize.y;
2025-01-20 05:41:45 -05:00
});
2025-01-14 17:29:48 -05:00
},
onRemove: (map) => {
const fog = document.getElementById('fog');
if (fog) {
fog.remove();
}
2025-01-20 05:41:45 -05:00
// Clean up event listener
map.off('resize');
2025-01-14 17:29:48 -05:00
}
});
}