tilfluktsrom/pwa/src/ui/compass-view.ts

89 lines
2.5 KiB
TypeScript
Raw Normal View History

/**
* Canvas-based direction arrow pointing toward the selected shelter.
* Ported from DirectionArrowView.kt same 7-point arrow polygon.
*
* Arrow rotation = shelterBearing - deviceHeading
*/
const ARROW_COLOR = '#FF6B35';
const OUTLINE_COLOR = '#FFFFFF';
const OUTLINE_WIDTH = 4;
let canvas: HTMLCanvasElement | null = null;
let ctx: CanvasRenderingContext2D | null = null;
let currentAngle = 0;
let animFrameId = 0;
/** Initialize the compass canvas inside the given container element. */
export function initCompass(container: HTMLElement): void {
canvas = document.createElement('canvas');
canvas.id = 'compass-canvas';
canvas.style.width = '100%';
canvas.style.height = '100%';
container.prepend(canvas);
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
}
function resizeCanvas(): void {
if (!canvas) return;
const rect = canvas.parentElement!.getBoundingClientRect();
canvas.width = rect.width * devicePixelRatio;
canvas.height = rect.height * devicePixelRatio;
draw();
}
/**
* Set the arrow direction in degrees.
* 0 = pointing up, positive = clockwise.
*/
export function setDirection(degrees: number): void {
currentAngle = degrees;
if (animFrameId) cancelAnimationFrame(animFrameId);
animFrameId = requestAnimationFrame(draw);
}
function draw(): void {
if (!canvas) return;
ctx = canvas.getContext('2d');
if (!ctx) return;
const w = canvas.width;
const h = canvas.height;
const cx = w / 2;
const cy = h / 2;
const size = Math.min(w, h) * 0.4;
ctx.clearRect(0, 0, w, h);
ctx.save();
ctx.translate(cx, cy);
ctx.rotate((currentAngle * Math.PI) / 180);
// 7-point arrow polygon (same geometry as DirectionArrowView.kt)
ctx.beginPath();
ctx.moveTo(0, -size); // tip
ctx.lineTo(size * 0.5, size * 0.3); // right wing
ctx.lineTo(size * 0.15, size * 0.1); // right notch
ctx.lineTo(size * 0.15, size * 0.7); // right tail
ctx.lineTo(-size * 0.15, size * 0.7); // left tail
ctx.lineTo(-size * 0.15, size * 0.1); // left notch
ctx.lineTo(-size * 0.5, size * 0.3); // left wing
ctx.closePath();
ctx.fillStyle = ARROW_COLOR;
ctx.fill();
ctx.strokeStyle = OUTLINE_COLOR;
ctx.lineWidth = OUTLINE_WIDTH;
ctx.stroke();
ctx.restore();
}
/** Clean up compass resources. */
export function destroyCompass(): void {
window.removeEventListener('resize', resizeCanvas);
if (animFrameId) cancelAnimationFrame(animFrameId);
canvas?.remove();
canvas = null;
ctx = null;
}