1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-19 22:58:07 +02:00

Add origin parameter to updateScale

This parameter allows defining which point should remain
fixed while scaling the document. It can be used, for example,
to implement "zoom around the cursor" or "zoom around
pinch center".

The logic was previously implemented in `web/app.js`, but
moving it to the viewer scaling utilities themselves makes it
easier to implement similar zooming functionalities in
other embedders.
This commit is contained in:
Nicolò Ribaudo 2024-05-21 16:38:46 +02:00
parent 161c7045f6
commit b7933d8750
No known key found for this signature in database
GPG key ID: AAFDA9101C58F338
3 changed files with 105 additions and 33 deletions

View file

@ -743,7 +743,7 @@ const PDFViewerApplication = {
return this._initializedCapability.promise;
},
updateZoom(steps, scaleFactor) {
updateZoom(steps, scaleFactor, origin) {
if (this.pdfViewer.isInPresentationMode) {
return;
}
@ -751,6 +751,7 @@ const PDFViewerApplication = {
drawingDelay: AppOptions.get("defaultZoomDelay"),
steps,
scaleFactor,
origin,
});
},
@ -2121,16 +2122,6 @@ const PDFViewerApplication = {
return newFactor;
},
_centerAtPos(previousScale, x, y) {
const { pdfViewer } = this;
const scaleDiff = pdfViewer.currentScale / previousScale - 1;
if (scaleDiff !== 0) {
const [top, left] = pdfViewer.containerTopLeft;
pdfViewer.container.scrollLeft += (x - left) * scaleDiff;
pdfViewer.container.scrollTop += (y - top) * scaleDiff;
}
},
/**
* Should be called *after* all pages have loaded, or if an error occurred,
* to unblock the "load" event; see https://bugzilla.mozilla.org/show_bug.cgi?id=1618553
@ -2607,6 +2598,7 @@ function webViewerWheel(evt) {
evt.deltaX === 0 &&
(Math.abs(scaleFactor - 1) < 0.05 || isBuiltInMac) &&
evt.deltaZ === 0;
const origin = [evt.clientX, evt.clientY];
if (
isPinchToZoom ||
@ -2625,14 +2617,13 @@ function webViewerWheel(evt) {
return;
}
const previousScale = pdfViewer.currentScale;
if (isPinchToZoom && supportsPinchToZoom) {
scaleFactor = PDFViewerApplication._accumulateFactor(
previousScale,
pdfViewer.currentScale,
scaleFactor,
"_wheelUnusedFactor"
);
PDFViewerApplication.updateZoom(null, scaleFactor);
PDFViewerApplication.updateZoom(null, scaleFactor, origin);
} else {
const delta = normalizeWheelEventDirection(evt);
@ -2664,13 +2655,8 @@ function webViewerWheel(evt) {
);
}
PDFViewerApplication.updateZoom(ticks);
PDFViewerApplication.updateZoom(ticks, null, origin);
}
// After scaling the page via zoomIn/zoomOut, the position of the upper-
// left corner is restored. When the mouse wheel is used, the position
// under the cursor should be restored instead.
PDFViewerApplication._centerAtPos(previousScale, evt.clientX, evt.clientY);
}
}
@ -2770,30 +2756,24 @@ function webViewerTouchMove(evt) {
evt.preventDefault();
const origin = [(page0X + page1X) / 2, (page0Y + page1Y) / 2];
const distance = Math.hypot(page0X - page1X, page0Y - page1Y) || 1;
const pDistance = Math.hypot(pTouch0X - pTouch1X, pTouch0Y - pTouch1Y) || 1;
const previousScale = pdfViewer.currentScale;
if (supportsPinchToZoom) {
const newScaleFactor = PDFViewerApplication._accumulateFactor(
previousScale,
pdfViewer.currentScale,
distance / pDistance,
"_touchUnusedFactor"
);
PDFViewerApplication.updateZoom(null, newScaleFactor);
PDFViewerApplication.updateZoom(null, newScaleFactor, origin);
} else {
const PIXELS_PER_LINE_SCALE = 30;
const ticks = PDFViewerApplication._accumulateTicks(
(distance - pDistance) / PIXELS_PER_LINE_SCALE,
"_touchUnusedTicks"
);
PDFViewerApplication.updateZoom(ticks);
PDFViewerApplication.updateZoom(ticks, null, origin);
}
PDFViewerApplication._centerAtPos(
previousScale,
(page0X + page1X) / 2,
(page0Y + page1Y) / 2
);
}
function webViewerTouchEnd(evt) {

View file

@ -1219,7 +1219,7 @@ class PDFViewer {
#setScaleUpdatePages(
newScale,
newValue,
{ noScroll = false, preset = false, drawingDelay = -1 }
{ noScroll = false, preset = false, drawingDelay = -1, origin = null }
) {
this._currentScaleValue = newValue.toString();
@ -1252,6 +1252,7 @@ class PDFViewer {
}, drawingDelay);
}
const previousScale = this._currentScale;
this._currentScale = newScale;
if (!noScroll) {
@ -1275,6 +1276,15 @@ class PDFViewer {
destArray: dest,
allowNegativeOffset: true,
});
if (Array.isArray(origin)) {
// If the origin of the scaling transform is specified, preserve its
// location on screen. If not specified, scaling will fix the top-left
// corner of the visible PDF area.
const scaleDiff = newScale / previousScale - 1;
const [top, left] = this.containerTopLeft;
this.container.scrollLeft += (origin[0] - left) * scaleDiff;
this.container.scrollTop += (origin[1] - top) * scaleDiff;
}
}
this.eventBus.dispatch("scalechanging", {
@ -2122,13 +2132,15 @@ class PDFViewer {
* @property {number} [drawingDelay]
* @property {number} [scaleFactor]
* @property {number} [steps]
* @property {Array} [origin] x and y coordinates of the scale
* transformation origin.
*/
/**
* Changes the current zoom level by the specified amount.
* @param {ChangeScaleOptions} [options]
*/
updateScale({ drawingDelay, scaleFactor = null, steps = null }) {
updateScale({ drawingDelay, scaleFactor = null, steps = null, origin }) {
if (steps === null && scaleFactor === null) {
throw new Error(
"Invalid updateScale options: either `steps` or `scaleFactor` must be provided."
@ -2149,7 +2161,7 @@ class PDFViewer {
} while (--steps > 0);
}
newScale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, newScale));
this.#setScale(newScale, { noScroll: false, drawingDelay });
this.#setScale(newScale, { noScroll: false, drawingDelay, origin });
}
/**