import { shallowRef } from 'vue'; import { removeGuide, state, updateGuide } from '../store'; // The ruler band size (must match Rulers' SIZE). Dropping a guide back over its ruler removes it. const RULER = 20; // Interactive guides drawn over the canvas. Each guide is a thin draggable strip: drag to // reposition, drag back onto its ruler (or double-click) to remove. New guides are pulled out // of the rulers (see Rulers.tsx). Positions are stored in iframe-content pixels and mapped to // the viewport via the current pan/zoom. export default function GuidesLayer() { const layer = shallowRef(); const startMove = (axis: 'x' | 'y', index: number): void => { const rect = layer.value?.getBoundingClientRect(); if (!rect) return; const move = (ev: PointerEvent): void => { const pos = axis === 'x' ? (ev.clientX - rect.left - state.panX) / state.zoom : (ev.clientY - rect.top - state.panY) / state.zoom; updateGuide(axis, index, pos); }; const up = (ev: PointerEvent): void => { window.removeEventListener('pointermove', move, true); window.removeEventListener('pointerup', up, true); // Released over the originating ruler band → remove the guide. const overRuler = axis === 'x' ? ev.clientY - rect.top < RULER : ev.clientX - rect.left < RULER; if (overRuler) removeGuide(axis, index); }; window.addEventListener('pointermove', move, true); window.addEventListener('pointerup', up, true); }; const begin = (e: { preventDefault(): void; stopPropagation(): void }, axis: 'x' | 'y', index: number): void => { e.preventDefault(); e.stopPropagation(); startMove(axis, index); }; return (
{state.guides.x.map((gx, i) => (
begin(e, 'x', i)} onDblclick={() => removeGuide('x', i)} >
{Math.round(gx)}
))} {state.guides.y.map((gy, i) => (
begin(e, 'y', i)} onDblclick={() => removeGuide('y', i)} >
{Math.round(gy)}
))}
); }