mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
zoom: editor zoom is a working tool — leaving any editor restores the view-mode viewport
Entering an editor snapshots the view-mode zoom+center (per space); leaving back to view brings it back and re-saves it to LS_ZOOM, so wheel zoom done inside the editors no longer leaks into the viewing zoom. Editor-to-editor switches and the per-space view zoom keep working as before. Smoke: smoke_zoom_out.mjs grows the 1.6 -> editor 2.5 -> 1.6 scenario (zoom, center, LS), the no-touch no-jump case and the space-switch persistence guard.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -48,6 +48,66 @@ out.floorIsHalf = await page.evaluate(() => { window.__card._resetZoom(); const
|
||||
c._applyView(0.1); return c._zoom; }) === 0.4; // clamped at the floor
|
||||
await page.evaluate(() => window.__card._resetZoom());
|
||||
|
||||
// -- editor zoom is a working tool, not the viewing intent ----------------
|
||||
// view 1.6 → devices editor 2.5 → back to view: the pre-editor viewport
|
||||
// (zoom AND center) comes back; the editor keeps its own zoom while open.
|
||||
out.editorZoomNotSaved = await page.evaluate(async () => {
|
||||
const c = window.__card;
|
||||
const raf2 = () => new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));
|
||||
const center = () => { const v = c._view; return [v.x + v.w / 2, v.y + v.h / 2]; };
|
||||
c._setMode('view');
|
||||
c._resetZoom();
|
||||
c._zoomAt(10, 10, 1.6); c._saveZoom(); // off-center on purpose
|
||||
const want = { zoom: c._zoom, c: center() };
|
||||
c._setMode('devices'); await raf2();
|
||||
c._zoomAt(10, 10, 2.5); c._saveZoom(); // off-center too: the center must not leak either
|
||||
const editorZoomFree = Math.abs(c._zoom - 2.5) < 0.01; // zooming inside stays
|
||||
c._setMode('view'); await raf2();
|
||||
const got = { zoom: c._zoom, c: center() };
|
||||
const ls = JSON.parse(localStorage.getItem('houseplan_card_zoom_v1') || '{}');
|
||||
return {
|
||||
editorZoomFree,
|
||||
zoomRestored: Math.abs(got.zoom - want.zoom) < 0.01,
|
||||
centerRestored: Math.hypot(got.c[0] - want.c[0], got.c[1] - want.c[1]) < 0.02,
|
||||
lsRestored: Math.abs((ls[c._space] || 1) - want.zoom) < 0.01,
|
||||
};
|
||||
});
|
||||
out.editorZoomFree = out.editorZoomNotSaved.editorZoomFree;
|
||||
out.viewZoomRestored = out.editorZoomNotSaved.zoomRestored;
|
||||
out.viewCenterRestored = out.editorZoomNotSaved.centerRestored;
|
||||
out.viewZoomBackInLs = out.editorZoomNotSaved.lsRestored;
|
||||
delete out.editorZoomNotSaved;
|
||||
|
||||
// -- editor entered and left without touching zoom: no jump ---------------
|
||||
out.untouchedEditorNoJump = await page.evaluate(async () => {
|
||||
const c = window.__card;
|
||||
const raf2 = () => new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));
|
||||
c._setMode('view');
|
||||
c._resetZoom();
|
||||
c._zoomAt(30, 30, 1.6); c._saveZoom();
|
||||
const before = { ...c._view };
|
||||
c._setMode('plan'); await raf2();
|
||||
c._setMode('view'); await raf2();
|
||||
const v = c._view;
|
||||
return Math.abs(c._zoom - 1.6) < 0.01
|
||||
&& Math.abs(v.x - before.x) < 0.005 && Math.abs(v.y - before.y) < 0.005
|
||||
&& Math.abs(v.w - before.w) < 0.005 && Math.abs(v.h - before.h) < 0.005;
|
||||
});
|
||||
|
||||
// -- the view zoom still survives a space switch, per space ---------------
|
||||
out.viewZoomSurvivesSpaceSwitch = await page.evaluate(async () => {
|
||||
const c = window.__card;
|
||||
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
c._setMode('view');
|
||||
c._resetZoom(); c._applyView(1.7); c._saveZoom();
|
||||
c._slideTo('garden', 'left'); await wait(350);
|
||||
const gardenGotOwnZoom = Math.abs(c._zoom - (JSON.parse(
|
||||
localStorage.getItem('houseplan_card_zoom_v1') || '{}').garden || 1)) < 0.01;
|
||||
c._slideTo('f1', 'right'); await wait(350);
|
||||
return gardenGotOwnZoom && Math.abs(c._zoom - 1.7) < 0.01;
|
||||
});
|
||||
await page.evaluate(() => window.__card._resetZoom());
|
||||
|
||||
// -- devices outside rooms stretch the default frame ---------------------
|
||||
out.devicesStretchFrame = await page.evaluate(() => {
|
||||
const c = window.__card;
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -246,6 +246,12 @@ class HouseplanCard extends LitElement {
|
||||
private _zoom = 1;
|
||||
private _view: { x: number; y: number; w: number; h: number } | null = null; // current SVG viewBox (vb coordinates)
|
||||
private _zoomBySpace: Record<string, number> = {};
|
||||
/**
|
||||
* View-mode viewport remembered on entering an editor. Editor zoom is a
|
||||
* working tool (zoom in to grab a vertex), not the user's intention for
|
||||
* viewing — leaving any editor brings the view-mode viewport back.
|
||||
*/
|
||||
private _viewModeSnap: { space: string; zoom: number; cx?: number; cy?: number } | null = null;
|
||||
private _pointers = new Map<number, { x: number; y: number }>();
|
||||
private _panStart: { sx: number; sy: number; vx: number; vy: number } | null = null;
|
||||
private _pinchStart: { dist: number; zoom: number } | null = null;
|
||||
@@ -1857,6 +1863,17 @@ class HouseplanCard extends LitElement {
|
||||
return;
|
||||
}
|
||||
const baseChanges = !this._spaceModel().bg && (mode === 'view') !== (this._mode === 'view');
|
||||
if (this._mode === 'view' && mode !== 'view') {
|
||||
// remember the view-mode viewport: whatever zooming happens inside the
|
||||
// editors is a working tool, not what the user wants to see afterwards
|
||||
const v = this._view;
|
||||
this._viewModeSnap = {
|
||||
space: this._space,
|
||||
zoom: this._zoom,
|
||||
cx: v ? v.x + v.w / 2 : undefined,
|
||||
cy: v ? v.y + v.h / 2 : undefined,
|
||||
};
|
||||
}
|
||||
this._mode = mode;
|
||||
if (baseChanges) {
|
||||
// refit against the new base: the editors measure from the full square,
|
||||
@@ -1865,6 +1882,22 @@ class HouseplanCard extends LitElement {
|
||||
this._zoom = 1;
|
||||
this._view = null; // updated() refits on the next frame
|
||||
}
|
||||
if (mode === 'view') {
|
||||
const snap = this._viewModeSnap;
|
||||
this._viewModeSnap = null;
|
||||
// restore only for the space the snapshot was taken in — after a space
|
||||
// switch inside the editor the saved per-space zoom already applies
|
||||
if (snap && snap.space === this._space) {
|
||||
this._zoom = snap.zoom;
|
||||
this._view = null;
|
||||
requestAnimationFrame(() => {
|
||||
if (!this._stageEl || this._mode !== 'view' || this._space !== snap.space) return;
|
||||
this._applyView(snap.zoom, snap.cx, snap.cy);
|
||||
this._saveZoom(); // editor wheel zoom wrote itself to LS_ZOOM — put the view zoom back
|
||||
this.requestUpdate();
|
||||
});
|
||||
}
|
||||
}
|
||||
this._path = [];
|
||||
this._cursorPt = null;
|
||||
this._tool = 'draw';
|
||||
|
||||
Reference in New Issue
Block a user