When showing Inspect view, fit images to client area initially
This commit is contained in:
parent
8cd74dd1b4
commit
e852547d01
2 changed files with 50 additions and 24 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
/* canvas-ui.js - Shared Canvas Logic (Mobile/Touch Ready) */
|
/* canvas-ui.js - Shared Canvas Logic (Mobile/Touch Ready + Resize Hook) */
|
||||||
|
|
||||||
class PanZoomCanvas {
|
class PanZoomCanvas {
|
||||||
constructor(id, onDraw, onClick, onDragPt) {
|
constructor(id, onDraw, onClick, onDragPt) {
|
||||||
|
|
@ -17,7 +17,10 @@ class PanZoomCanvas {
|
||||||
this.onClick = onClick;
|
this.onClick = onClick;
|
||||||
this.onDragPt = onDragPt;
|
this.onDragPt = onDragPt;
|
||||||
this.onMouseMove = null;
|
this.onMouseMove = null;
|
||||||
this.onPointerDown = null; // New Hook
|
this.onPointerDown = null;
|
||||||
|
|
||||||
|
// New: Resize Callback hook
|
||||||
|
this.onResize = null;
|
||||||
|
|
||||||
this.evCache = [];
|
this.evCache = [];
|
||||||
this.prevDiff = -1;
|
this.prevDiff = -1;
|
||||||
|
|
@ -30,6 +33,8 @@ class PanZoomCanvas {
|
||||||
this.canvas.width = this.container.clientWidth;
|
this.canvas.width = this.container.clientWidth;
|
||||||
this.canvas.height = this.container.clientHeight;
|
this.canvas.height = this.container.clientHeight;
|
||||||
this.draw();
|
this.draw();
|
||||||
|
// Trigger Hook
|
||||||
|
if (this.onResize) this.onResize(this.canvas.width, this.canvas.height);
|
||||||
}
|
}
|
||||||
}).observe(this.container);
|
}).observe(this.container);
|
||||||
this.initEvents();
|
this.initEvents();
|
||||||
|
|
@ -75,7 +80,6 @@ class PanZoomCanvas {
|
||||||
this.evCache.push(e);
|
this.evCache.push(e);
|
||||||
this.totalDragDist = 0;
|
this.totalDragDist = 0;
|
||||||
|
|
||||||
// Trigger Hook if defined (Before internal logic)
|
|
||||||
if (this.onPointerDown) this.onPointerDown(e);
|
if (this.onPointerDown) this.onPointerDown(e);
|
||||||
|
|
||||||
const coords = this.getImgCoords(e.clientX, e.clientY);
|
const coords = this.getImgCoords(e.clientX, e.clientY);
|
||||||
|
|
@ -100,7 +104,6 @@ class PanZoomCanvas {
|
||||||
if (index > -1) this.evCache[index] = e;
|
if (index > -1) this.evCache[index] = e;
|
||||||
|
|
||||||
if (this.evCache.length === 2) {
|
if (this.evCache.length === 2) {
|
||||||
// Pinch Zoom
|
|
||||||
const dx = this.evCache[0].clientX - this.evCache[1].clientX;
|
const dx = this.evCache[0].clientX - this.evCache[1].clientX;
|
||||||
const dy = this.evCache[0].clientY - this.evCache[1].clientY;
|
const dy = this.evCache[0].clientY - this.evCache[1].clientY;
|
||||||
const curDiff = Math.hypot(dx, dy);
|
const curDiff = Math.hypot(dx, dy);
|
||||||
|
|
@ -112,14 +115,13 @@ class PanZoomCanvas {
|
||||||
this.zoomAt(cx, cy, factor);
|
this.zoomAt(cx, cy, factor);
|
||||||
}
|
}
|
||||||
this.prevDiff = curDiff;
|
this.prevDiff = curDiff;
|
||||||
this.totalDragDist += 20; // Ensure pinch doesn't trigger click
|
this.totalDragDist += 20;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isDragging && this.evCache.length === 1) {
|
if (this.isDragging && this.evCache.length === 1) {
|
||||||
const dx = e.clientX - this.lm.x;
|
const dx = e.clientX - this.lm.x;
|
||||||
const dy = e.clientY - this.lm.y;
|
const dy = e.clientY - this.lm.y;
|
||||||
|
|
||||||
this.totalDragDist += Math.hypot(dx, dy);
|
this.totalDragDist += Math.hypot(dx, dy);
|
||||||
|
|
||||||
if (this.activePtIdx !== -1) {
|
if (this.activePtIdx !== -1) {
|
||||||
|
|
@ -146,7 +148,6 @@ class PanZoomCanvas {
|
||||||
if (this.evCache.length < 2) this.prevDiff = -1;
|
if (this.evCache.length < 2) this.prevDiff = -1;
|
||||||
|
|
||||||
if (this.evCache.length === 0) {
|
if (this.evCache.length === 0) {
|
||||||
// Threshold increased to 20px for mobile tap tolerance
|
|
||||||
if (this.totalDragDist < 20) {
|
if (this.totalDragDist < 20) {
|
||||||
if (this.activePtIdx === -1 && this.onClick) {
|
if (this.activePtIdx === -1 && this.onClick) {
|
||||||
const coords = this.getImgCoords(e.clientX, e.clientY);
|
const coords = this.getImgCoords(e.clientX, e.clientY);
|
||||||
|
|
|
||||||
|
|
@ -71,12 +71,18 @@ class Inspector {
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderGrid() {
|
async renderGrid() {
|
||||||
// --- 1. Snapshot State (Zoom/Pan) ---
|
// 1. Snapshot State
|
||||||
|
// We save state ONLY if the viewer exists.
|
||||||
|
// We also capture 'interacted' status to know if we should restore specific zoom or just auto-fit.
|
||||||
const savedStates = {};
|
const savedStates = {};
|
||||||
if (this.viewers) {
|
if (this.viewers) {
|
||||||
Object.entries(this.viewers).forEach(([id, v]) => {
|
Object.entries(this.viewers).forEach(([id, v]) => {
|
||||||
// We clone the object to ensure safety, though not strictly required
|
if (v.t) {
|
||||||
if (v.t) savedStates[id] = { ...v.t };
|
savedStates[id] = {
|
||||||
|
t: { ...v.t },
|
||||||
|
interacted: v.userInteracted || false // Capture the flag from the instance
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,13 +137,12 @@ class Inspector {
|
||||||
this.grid.appendChild(cell);
|
this.grid.appendChild(cell);
|
||||||
|
|
||||||
let wasActiveBeforeDown = false;
|
let wasActiveBeforeDown = false;
|
||||||
|
let stateRestored = false;
|
||||||
|
|
||||||
const viewer = new PanZoomCanvas(cvs.id,
|
const viewer = new PanZoomCanvas(cvs.id,
|
||||||
(ctx, k) => this.drawOverlay(id, ctx, k),
|
(ctx, k) => this.drawOverlay(id, ctx, k),
|
||||||
|
|
||||||
async (x, y, e) => {
|
async (x, y, e) => {
|
||||||
if (e.button !== 0) return;
|
if (e.button !== 0) return;
|
||||||
|
|
||||||
if (wasActiveBeforeDown) {
|
if (wasActiveBeforeDown) {
|
||||||
const hit = await this.handleNodeClick(id, x, y);
|
const hit = await this.handleNodeClick(id, x, y);
|
||||||
if (!hit) {
|
if (!hit) {
|
||||||
|
|
@ -150,10 +155,16 @@ class Inspector {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Initialize interaction flag on the instance
|
||||||
|
viewer.userInteracted = false;
|
||||||
|
|
||||||
|
// --- EVENT HANDLERS ---
|
||||||
|
|
||||||
viewer.onPointerDown = (e) => {
|
viewer.onPointerDown = (e) => {
|
||||||
|
viewer.userInteracted = true; // Mark as dirty/manual
|
||||||
|
|
||||||
if (e.isPrimary || e.button === 0) {
|
if (e.isPrimary || e.button === 0) {
|
||||||
wasActiveBeforeDown = (this.masterId === id);
|
wasActiveBeforeDown = (this.masterId === id);
|
||||||
|
|
||||||
if (this.masterId !== id) {
|
if (this.masterId !== id) {
|
||||||
this.masterId = id;
|
this.masterId = id;
|
||||||
}
|
}
|
||||||
|
|
@ -162,10 +173,33 @@ class Inspector {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Detect wheel usage to stop auto-fit
|
||||||
|
cvs.addEventListener('wheel', () => { viewer.userInteracted = true; });
|
||||||
|
|
||||||
viewer.onMouseMove = (x, y) => {
|
viewer.onMouseMove = (x, y) => {
|
||||||
if(this.masterId === id) this.syncCursors(id, x, y);
|
if(this.masterId === id) this.syncCursors(id, x, y);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --- SMART RESIZE/FIT LOGIC ---
|
||||||
|
const updateView = () => {
|
||||||
|
if (!viewer.bmp || cvs.width < 20 || cvs.height < 20) return;
|
||||||
|
|
||||||
|
// 1. Restore State (Only if user had manually interacted before)
|
||||||
|
if (savedStates[id] && savedStates[id].interacted && !stateRestored) {
|
||||||
|
viewer.t = savedStates[id].t;
|
||||||
|
stateRestored = true;
|
||||||
|
viewer.userInteracted = true; // Keep it marked as manual
|
||||||
|
viewer.draw();
|
||||||
|
}
|
||||||
|
// 2. Auto-Fit (Default behavior, continues on resize until user interacts)
|
||||||
|
else if (!viewer.userInteracted) {
|
||||||
|
viewer.fit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Attempt fit on Resize
|
||||||
|
viewer.onResize = (w, h) => updateView();
|
||||||
|
|
||||||
cvs.addEventListener('contextmenu', (e) => {
|
cvs.addEventListener('contextmenu', (e) => {
|
||||||
e.preventDefault(); e.stopPropagation();
|
e.preventDefault(); e.stopPropagation();
|
||||||
this.masterId = null;
|
this.masterId = null;
|
||||||
|
|
@ -180,18 +214,9 @@ class Inspector {
|
||||||
try {
|
try {
|
||||||
const bmp = await createImageBitmap(imgRec.blob);
|
const bmp = await createImageBitmap(imgRec.blob);
|
||||||
viewer.setImage(bmp);
|
viewer.setImage(bmp);
|
||||||
if (cell.clientWidth && cell.clientHeight) {
|
|
||||||
viewer.canvas.width = cell.clientWidth;
|
|
||||||
viewer.canvas.height = cell.clientHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- 2. Restore State or Fit ---
|
// Attempt fit on Load
|
||||||
if (savedStates[id]) {
|
updateView();
|
||||||
viewer.t = savedStates[id];
|
|
||||||
viewer.draw(); // Force redraw with restored transform
|
|
||||||
} else {
|
|
||||||
viewer.fit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(imgRec.name.toLowerCase().includes('bot') && !imgRec.name.toLowerCase().includes('top')) {
|
if(imgRec.name.toLowerCase().includes('bot') && !imgRec.name.toLowerCase().includes('top')) {
|
||||||
viewer.setMirror(true);
|
viewer.setMirror(true);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue