Merge pull request #15909 from calixteman/pinch_trackpad
Add support for smooth pinch-to-zoom on a trackpad (bug 1659492)
This commit is contained in:
commit
dafcf82365
210
web/app.js
210
web/app.js
@ -217,7 +217,9 @@ const PDFViewerApplication = {
|
||||
_contentLength: null,
|
||||
_saveInProgress: false,
|
||||
_wheelUnusedTicks: 0,
|
||||
_wheelUnusedFactor: 1,
|
||||
_touchUnusedTicks: 0,
|
||||
_touchUnusedFactor: 1,
|
||||
_PDFBug: null,
|
||||
_hasAnnotationEditors: false,
|
||||
_title: document.title,
|
||||
@ -2058,6 +2060,23 @@ const PDFViewerApplication = {
|
||||
return wholeTicks;
|
||||
},
|
||||
|
||||
_accumulateFactor(previousScale, factor, prop) {
|
||||
if (factor === 1) {
|
||||
return 1;
|
||||
}
|
||||
// If the direction changed, reset the accumulated factor.
|
||||
if ((this[prop] > 1 && factor < 1) || (this[prop] < 1 && factor > 1)) {
|
||||
this[prop] = 1;
|
||||
}
|
||||
|
||||
const newFactor =
|
||||
Math.floor(previousScale * factor * this[prop] * 100) /
|
||||
(100 * previousScale);
|
||||
this[prop] = factor / newFactor;
|
||||
|
||||
return newFactor;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -2628,8 +2647,11 @@ function setZoomDisabledTimeout() {
|
||||
}
|
||||
|
||||
function webViewerWheel(evt) {
|
||||
const { pdfViewer, supportedMouseWheelZoomModifierKeys } =
|
||||
PDFViewerApplication;
|
||||
const {
|
||||
pdfViewer,
|
||||
supportedMouseWheelZoomModifierKeys,
|
||||
supportsPinchToZoom,
|
||||
} = PDFViewerApplication;
|
||||
|
||||
if (pdfViewer.isInPresentationMode) {
|
||||
return;
|
||||
@ -2653,45 +2675,61 @@ function webViewerWheel(evt) {
|
||||
return;
|
||||
}
|
||||
|
||||
// It is important that we query deltaMode before delta{X,Y}, so that
|
||||
// Firefox doesn't switch to DOM_DELTA_PIXEL mode for compat with other
|
||||
// browsers, see https://bugzilla.mozilla.org/show_bug.cgi?id=1392460.
|
||||
const deltaMode = evt.deltaMode;
|
||||
const delta = normalizeWheelEventDirection(evt);
|
||||
const previousScale = pdfViewer.currentScale;
|
||||
if (isPinchToZoom && supportsPinchToZoom) {
|
||||
// The following formula is a bit strange but it comes from:
|
||||
// https://searchfox.org/mozilla-central/rev/d62c4c4d5547064487006a1506287da394b64724/widget/InputData.cpp#618-626
|
||||
let scaleFactor = Math.exp(-evt.deltaY / 100);
|
||||
scaleFactor = PDFViewerApplication._accumulateFactor(
|
||||
previousScale,
|
||||
scaleFactor,
|
||||
"_wheelUnusedFactor"
|
||||
);
|
||||
if (scaleFactor < 1) {
|
||||
PDFViewerApplication.zoomOut(null, scaleFactor);
|
||||
} else if (scaleFactor > 1) {
|
||||
PDFViewerApplication.zoomIn(null, scaleFactor);
|
||||
}
|
||||
} else {
|
||||
// It is important that we query deltaMode before delta{X,Y}, so that
|
||||
// Firefox doesn't switch to DOM_DELTA_PIXEL mode for compat with other
|
||||
// browsers, see https://bugzilla.mozilla.org/show_bug.cgi?id=1392460.
|
||||
const deltaMode = evt.deltaMode;
|
||||
const delta = normalizeWheelEventDirection(evt);
|
||||
|
||||
let ticks = 0;
|
||||
if (
|
||||
deltaMode === WheelEvent.DOM_DELTA_LINE ||
|
||||
deltaMode === WheelEvent.DOM_DELTA_PAGE
|
||||
) {
|
||||
// For line-based devices, use one tick per event, because different
|
||||
// OSs have different defaults for the number lines. But we generally
|
||||
// want one "clicky" roll of the wheel (which produces one event) to
|
||||
// adjust the zoom by one step.
|
||||
if (Math.abs(delta) >= 1) {
|
||||
ticks = Math.sign(delta);
|
||||
let ticks = 0;
|
||||
if (
|
||||
deltaMode === WheelEvent.DOM_DELTA_LINE ||
|
||||
deltaMode === WheelEvent.DOM_DELTA_PAGE
|
||||
) {
|
||||
// For line-based devices, use one tick per event, because different
|
||||
// OSs have different defaults for the number lines. But we generally
|
||||
// want one "clicky" roll of the wheel (which produces one event) to
|
||||
// adjust the zoom by one step.
|
||||
if (Math.abs(delta) >= 1) {
|
||||
ticks = Math.sign(delta);
|
||||
} else {
|
||||
// If we're getting fractional lines (I can't think of a scenario
|
||||
// this might actually happen), be safe and use the accumulator.
|
||||
ticks = PDFViewerApplication._accumulateTicks(
|
||||
delta,
|
||||
"_wheelUnusedTicks"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// If we're getting fractional lines (I can't think of a scenario
|
||||
// this might actually happen), be safe and use the accumulator.
|
||||
// pixel-based devices
|
||||
const PIXELS_PER_LINE_SCALE = 30;
|
||||
ticks = PDFViewerApplication._accumulateTicks(
|
||||
delta,
|
||||
delta / PIXELS_PER_LINE_SCALE,
|
||||
"_wheelUnusedTicks"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// pixel-based devices
|
||||
const PIXELS_PER_LINE_SCALE = 30;
|
||||
ticks = PDFViewerApplication._accumulateTicks(
|
||||
delta / PIXELS_PER_LINE_SCALE,
|
||||
"_wheelUnusedTicks"
|
||||
);
|
||||
}
|
||||
|
||||
if (ticks < 0) {
|
||||
PDFViewerApplication.zoomOut(-ticks);
|
||||
} else if (ticks > 0) {
|
||||
PDFViewerApplication.zoomIn(ticks);
|
||||
if (ticks < 0) {
|
||||
PDFViewerApplication.zoomOut(-ticks);
|
||||
} else if (ticks > 0) {
|
||||
PDFViewerApplication.zoomIn(ticks);
|
||||
}
|
||||
}
|
||||
|
||||
const currentScale = pdfViewer.currentScale;
|
||||
@ -2725,12 +2763,15 @@ function webViewerTouchStart(evt) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [touch0, touch1] = evt.touches;
|
||||
let [touch0, touch1] = evt.touches;
|
||||
if (touch0.identifier > touch1.identifier) {
|
||||
[touch0, touch1] = [touch1, touch0];
|
||||
}
|
||||
PDFViewerApplication._touchInfo = {
|
||||
centerX: (touch0.pageX + touch1.pageX) / 2,
|
||||
centerY: (touch0.pageY + touch1.pageY) / 2,
|
||||
distance:
|
||||
Math.hypot(touch0.pageX - touch1.pageX, touch0.pageY - touch1.pageY) || 1,
|
||||
touch0X: touch0.pageX,
|
||||
touch0Y: touch0.pageY,
|
||||
touch1X: touch1.pageX,
|
||||
touch1Y: touch1.pageY,
|
||||
};
|
||||
}
|
||||
|
||||
@ -2740,42 +2781,88 @@ function webViewerTouchMove(evt) {
|
||||
}
|
||||
|
||||
const { pdfViewer, _touchInfo, supportsPinchToZoom } = PDFViewerApplication;
|
||||
const [touch0, touch1] = evt.touches;
|
||||
let [touch0, touch1] = evt.touches;
|
||||
if (touch0.identifier > touch1.identifier) {
|
||||
[touch0, touch1] = [touch1, touch0];
|
||||
}
|
||||
const { pageX: page0X, pageY: page0Y } = touch0;
|
||||
const { pageX: page1X, pageY: page1Y } = touch1;
|
||||
const distance = Math.hypot(page0X - page1X, page0Y - page1Y) || 1;
|
||||
const { distance: previousDistance } = _touchInfo;
|
||||
const scaleFactor = distance / previousDistance;
|
||||
const {
|
||||
touch0X: pTouch0X,
|
||||
touch0Y: pTouch0Y,
|
||||
touch1X: pTouch1X,
|
||||
touch1Y: pTouch1Y,
|
||||
} = _touchInfo;
|
||||
|
||||
if (supportsPinchToZoom && Math.abs(scaleFactor - 1) <= 1e-2) {
|
||||
// Scale increase/decrease isn't significant enough.
|
||||
if (
|
||||
Math.abs(pTouch0X - page0X) <= 1 &&
|
||||
Math.abs(pTouch0Y - page0Y) <= 1 &&
|
||||
Math.abs(pTouch1X - page1X) <= 1 &&
|
||||
Math.abs(pTouch1Y - page1Y) <= 1
|
||||
) {
|
||||
// Touches are really too close and it's hard do some basic
|
||||
// geometry in order to guess something.
|
||||
return;
|
||||
}
|
||||
|
||||
const { centerX, centerY } = _touchInfo;
|
||||
const diff0X = page0X - centerX;
|
||||
const diff1X = page1X - centerX;
|
||||
const diff0Y = page0Y - centerY;
|
||||
const diff1Y = page1Y - centerY;
|
||||
const dotProduct = diff0X * diff1X + diff0Y * diff1Y;
|
||||
if (dotProduct >= 0) {
|
||||
// The two touches go in almost the same direction.
|
||||
return;
|
||||
_touchInfo.touch0X = page0X;
|
||||
_touchInfo.touch0Y = page0Y;
|
||||
_touchInfo.touch1X = page1X;
|
||||
_touchInfo.touch1Y = page1Y;
|
||||
|
||||
if (pTouch0X === page0X && pTouch0Y === page0Y) {
|
||||
// First touch is fixed, if the vectors are collinear then we've a pinch.
|
||||
const v1X = pTouch1X - page0X;
|
||||
const v1Y = pTouch1Y - page0Y;
|
||||
const v2X = page1X - page0X;
|
||||
const v2Y = page1Y - page0Y;
|
||||
const det = v1X * v2Y - v1Y * v2X;
|
||||
// 0.02 is approximatively sin(0.15deg).
|
||||
if (Math.abs(det) > 0.02 * Math.hypot(v1X, v1Y) * Math.hypot(v2X, v2Y)) {
|
||||
return;
|
||||
}
|
||||
} else if (pTouch1X === page1X && pTouch1Y === page1Y) {
|
||||
// Second touch is fixed, if the vectors are collinear then we've a pinch.
|
||||
const v1X = pTouch0X - page1X;
|
||||
const v1Y = pTouch0Y - page1Y;
|
||||
const v2X = page0X - page1X;
|
||||
const v2Y = page0Y - page1Y;
|
||||
const det = v1X * v2Y - v1Y * v2X;
|
||||
if (Math.abs(det) > 0.02 * Math.hypot(v1X, v1Y) * Math.hypot(v2X, v2Y)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const diff0X = page0X - pTouch0X;
|
||||
const diff1X = page1X - pTouch1X;
|
||||
const diff0Y = page0Y - pTouch0Y;
|
||||
const diff1Y = page1Y - pTouch1Y;
|
||||
const dotProduct = diff0X * diff1X + diff0Y * diff1Y;
|
||||
if (dotProduct >= 0) {
|
||||
// The two touches go in almost the same direction.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
evt.preventDefault();
|
||||
|
||||
const distance = Math.hypot(page0X - page1X, page0Y - page1Y) || 1;
|
||||
const pDistance = Math.hypot(pTouch0X - pTouch1X, pTouch0Y - pTouch1Y) || 1;
|
||||
const previousScale = pdfViewer.currentScale;
|
||||
if (supportsPinchToZoom) {
|
||||
if (scaleFactor < 1) {
|
||||
PDFViewerApplication.zoomOut(null, scaleFactor);
|
||||
} else {
|
||||
PDFViewerApplication.zoomIn(null, scaleFactor);
|
||||
const newScaleFactor = PDFViewerApplication._accumulateFactor(
|
||||
previousScale,
|
||||
distance / pDistance,
|
||||
"_touchUnusedFactor"
|
||||
);
|
||||
if (newScaleFactor < 1) {
|
||||
PDFViewerApplication.zoomOut(null, newScaleFactor);
|
||||
} else if (newScaleFactor > 1) {
|
||||
PDFViewerApplication.zoomIn(null, newScaleFactor);
|
||||
}
|
||||
} else {
|
||||
const PIXELS_PER_LINE_SCALE = 30;
|
||||
const ticks = PDFViewerApplication._accumulateTicks(
|
||||
(distance - previousDistance) / PIXELS_PER_LINE_SCALE,
|
||||
(distance - pDistance) / PIXELS_PER_LINE_SCALE,
|
||||
"_touchUnusedTicks"
|
||||
);
|
||||
if (ticks < 0) {
|
||||
@ -2788,9 +2875,8 @@ function webViewerTouchMove(evt) {
|
||||
const currentScale = pdfViewer.currentScale;
|
||||
if (previousScale !== currentScale) {
|
||||
const scaleCorrectionFactor = currentScale / previousScale - 1;
|
||||
const newCenterX = (_touchInfo.centerX = (page0X + page1X) / 2);
|
||||
const newCenterY = (_touchInfo.centerY = (page0Y + page1Y) / 2);
|
||||
_touchInfo.distance = distance;
|
||||
const newCenterX = (page0X + page1X) / 2;
|
||||
const newCenterY = (page0Y + page1Y) / 2;
|
||||
|
||||
const [top, left] = pdfViewer.containerTopLeft;
|
||||
const dx = newCenterX - left;
|
||||
@ -2807,8 +2893,8 @@ function webViewerTouchEnd(evt) {
|
||||
|
||||
evt.preventDefault();
|
||||
PDFViewerApplication._touchInfo = null;
|
||||
PDFViewerApplication.pdfViewer.refresh();
|
||||
PDFViewerApplication._touchUnusedTicks = 0;
|
||||
PDFViewerApplication._touchUnusedFactor = 1;
|
||||
}
|
||||
|
||||
function webViewerClick(evt) {
|
||||
|
Loading…
Reference in New Issue
Block a user