maker: zoom in sovrimpressione, pan col puntatore e viewport che segue l'immagine (Refs #104)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jbf7qKuxsnAVjxthRy7dnB
This commit is contained in:
@@ -41,6 +41,9 @@ function dxfRecipeWizard(taskId, csrf, msgs) {
|
||||
imageSource: '', // #104: '' | 'file' | 'camera'; il campo mm/px vale solo per 'file'
|
||||
tested: false, // #104: true dopo una Prova (anche fallita): mostra la sezione del risultato
|
||||
zoom: 1, // #104: scala dell'immagine di prova (1 = pixel nativi)
|
||||
fitZoom: 1, // #104: zoom di Adatta (sotto o pari a questo il viewport segue l'immagine)
|
||||
panning: false, // #104: trascinamento in corso
|
||||
_pan: null, // #104: {x, y, left, top} al pointerdown
|
||||
resPx: null, // #104: {w, h} in pixel dell'immagine di prova (per la dimensione del wrapper)
|
||||
runPxPerMm: null, // #104: scala usata nell'ultima Prova (null = calibrazione stazione)
|
||||
runBandMm: null, // #104: banda usata nell'ultima Prova
|
||||
@@ -412,6 +415,32 @@ function dxfRecipeWizard(taskId, csrf, msgs) {
|
||||
get zoomPct() { return Math.round(this.zoom * 100) + ' %'; },
|
||||
get zoomW() { return this.resPx ? this.resPx.w * this.zoom : 0; },
|
||||
get zoomH() { return this.resPx ? this.resPx.h * this.zoom : 0; },
|
||||
// #104: altezza massima del viewport = min(70vh, 520px)
|
||||
_maxVpH() { return Math.min(0.7 * (typeof window !== 'undefined' && window.innerHeight ? window.innerHeight : 800), 520); },
|
||||
// #104: con zoom <= Adatta il viewport e' alto come l'immagine scalata (min 200px), altrimenti al massimo
|
||||
get vpHeight() {
|
||||
const max = this._maxVpH();
|
||||
if (!this.resPx || this.zoom > this.fitZoom * 1.0001) return max;
|
||||
return Math.min(max, Math.max(200, this.zoomH));
|
||||
},
|
||||
// #104: l'immagine e' piu' grande dello spazio: si puo' trascinare
|
||||
get canPan() { return !!this.resPx && this.zoom > this.fitZoom * 1.0001; },
|
||||
get panCursor() { return this.canPan ? (this.panning ? 'grabbing' : 'grab') : 'default'; },
|
||||
// #104: pan col puntatore (mouse e touch): scroll del viewport di quanto si muove il puntatore
|
||||
onPanDown(ev) {
|
||||
const vp = this.$refs.viewport;
|
||||
if (!vp || (ev.pointerType === 'mouse' && ev.button)) return;
|
||||
this._pan = { x: ev.clientX, y: ev.clientY, left: vp.scrollLeft, top: vp.scrollTop };
|
||||
this.panning = true;
|
||||
try { vp.setPointerCapture(ev.pointerId); } catch (e) { /* puntatore gia' rilasciato */ }
|
||||
},
|
||||
onPanMove(ev) {
|
||||
const vp = this.$refs.viewport;
|
||||
if (!this._pan || !vp) return;
|
||||
vp.scrollLeft = this._pan.left - (ev.clientX - this._pan.x);
|
||||
vp.scrollTop = this._pan.top - (ev.clientY - this._pan.y);
|
||||
},
|
||||
onPanEnd() { this._pan = null; this.panning = false; },
|
||||
zoomBy(f) {
|
||||
const vp = this.$refs.viewport;
|
||||
const old = this.zoom;
|
||||
@@ -423,11 +452,13 @@ function dxfRecipeWizard(taskId, csrf, msgs) {
|
||||
this.zoom = z;
|
||||
if (vp) this.$nextTick(() => { vp.scrollLeft = cx * z - vp.clientWidth / 2; vp.scrollTop = cy * z - vp.clientHeight / 2; });
|
||||
},
|
||||
// #104: Fit = tutta l'immagine nel viewport
|
||||
// #104: Fit = tutta l'immagine nel viewport (altezza massima, non quella corrente che segue l'immagine)
|
||||
fit() {
|
||||
const vp = this.$refs.viewport, img = this.$refs.img;
|
||||
if (!vp || !img || !img.naturalWidth || !vp.clientWidth || !vp.clientHeight) return;
|
||||
this.zoom = Math.min(8, Math.max(0.05, Math.min(vp.clientWidth / img.naturalWidth, vp.clientHeight / img.naturalHeight)));
|
||||
if (!vp || !img || !img.naturalWidth || !vp.clientWidth) return;
|
||||
const z = Math.min(8, Math.max(0.05, Math.min(vp.clientWidth / img.naturalWidth, this._maxVpH() / img.naturalHeight)));
|
||||
this.zoom = z;
|
||||
this.fitZoom = z;
|
||||
vp.scrollLeft = 0; vp.scrollTop = 0;
|
||||
},
|
||||
// #104: Ctrl+rotella = zoom
|
||||
|
||||
Reference in New Issue
Block a user