feat: minimap navigation overlay + ruler/protractor property controls
Minimap: - Auto-shows in bottom-right corner when zoom > 1.05 - Renders full board content at scale (background + all strokes) - Purple viewport indicator with darkened outer areas - Click/drag to jump-pan the viewport - Cleaned up on destroy() Ruler/protractor property controls: - Rotation handle (purple ↺) — drag to rotate around origin - Resize handle (cyan ↔) — drag to change length/radius - Protractor now supports rotation via ctx.rotate(ov.angle) - Floating props panel in toolbar: angle° and length/radius inputs - Panel auto-shows on first click/drag, hides when overlay toggled off - Canvas-space hit testing with rotation-aware local coordinates Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+23
-17
@@ -2320,35 +2320,41 @@ class Whiteboard {
|
||||
this._mmCanvas.style.display = visible ? 'block' : 'none';
|
||||
if (!visible) return;
|
||||
|
||||
const mm = this._mmCtx;
|
||||
const MW = 192, MH = 108;
|
||||
mm.clearRect(0, 0, MW, MH);
|
||||
const mm = this._mmCtx;
|
||||
const MW = 192, MH = 108;
|
||||
|
||||
// Blit the static layer (full board content) scaled to minimap size
|
||||
mm.drawImage(this._canvas, 0, 0, MW, MH);
|
||||
// Render full board at zoom=1 (same approach as renderThumbnail)
|
||||
const [sw, sh, sz, spx, spy] = [this._cssW, this._cssH, this._zoom, this._panVX, this._panVY];
|
||||
this._cssW = MW; this._cssH = MH;
|
||||
this._zoom = 1; this._panVX = 0; this._panVY = 0;
|
||||
this._renderBg(mm);
|
||||
if (this._template && this._template !== 'blank') this._renderTemplate(mm);
|
||||
for (const s of this._strokes) this._renderStroke(mm, s);
|
||||
// Restore
|
||||
this._cssW = sw; this._cssH = sh; this._zoom = sz; this._panVX = spx; this._panVY = spy;
|
||||
|
||||
// Darken areas outside the viewport with a subtle vignette
|
||||
const vpW = MW / this._zoom;
|
||||
const vpH = MH / this._zoom;
|
||||
const vpX = (this._panVX / Whiteboard.VW) * MW;
|
||||
const vpY = (this._panVY / Whiteboard.VH) * MH;
|
||||
// Viewport indicator
|
||||
const vpW = MW / sz;
|
||||
const vpH = MH / sz;
|
||||
const vpX = (spx / Whiteboard.VW) * MW;
|
||||
const vpY = (spy / Whiteboard.VH) * MH;
|
||||
|
||||
// Dark overlay on non-viewport areas
|
||||
mm.fillStyle = 'rgba(0,0,0,0.42)';
|
||||
mm.fillRect(0, 0, MW, vpY); // top strip
|
||||
mm.fillRect(0, vpY + vpH, MW, MH - vpY - vpH); // bottom strip
|
||||
mm.fillRect(0, vpY, vpX, vpH); // left strip
|
||||
mm.fillRect(vpX + vpW, vpY, MW - vpX - vpW, vpH); // right strip
|
||||
mm.fillRect(0, 0, MW, vpY);
|
||||
mm.fillRect(0, vpY + vpH, MW, MH - vpY - vpH);
|
||||
mm.fillRect(0, vpY, vpX, vpH);
|
||||
mm.fillRect(vpX + vpW, vpY, MW - vpX - vpW, vpH);
|
||||
|
||||
// Viewport border
|
||||
mm.strokeStyle = 'rgba(155,93,229,0.95)';
|
||||
mm.lineWidth = 1.5;
|
||||
mm.strokeRect(vpX, vpY, vpW, vpH);
|
||||
|
||||
// Current position crosshair at viewport center
|
||||
// Crosshair at viewport center
|
||||
const cx = vpX + vpW / 2, cy = vpY + vpH / 2;
|
||||
mm.strokeStyle = 'rgba(155,93,229,0.55)';
|
||||
mm.lineWidth = 0.7;
|
||||
mm.strokeStyle = 'rgba(155,93,229,0.6)';
|
||||
mm.lineWidth = 0.8;
|
||||
mm.beginPath(); mm.moveTo(cx - 5, cy); mm.lineTo(cx + 5, cy); mm.stroke();
|
||||
mm.beginPath(); mm.moveTo(cx, cy - 5); mm.lineTo(cx, cy + 5); mm.stroke();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user