@progress/kendo-pdfviewer-common 1.0.0-develop.1 → 1.0.0-develop.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -47,6 +47,7 @@ export class PdfViewer extends Component {
|
|
|
47
47
|
maxZoom: 4,
|
|
48
48
|
zoomRate: 0.25,
|
|
49
49
|
zoomLevel: DEFAULT_ZOOM_LEVEL,
|
|
50
|
+
pinchToZoom: true,
|
|
50
51
|
zoomBeforePrint: false,
|
|
51
52
|
zoomLevelForPrint: 3,
|
|
52
53
|
renderForms: false,
|
|
@@ -148,6 +149,14 @@ export class PdfViewer extends Component {
|
|
|
148
149
|
this.state = {};
|
|
149
150
|
this.pdfDocument = null;
|
|
150
151
|
this.pages = [];
|
|
152
|
+
// Pinch-to-zoom state
|
|
153
|
+
this._isCtrlKeyDown = false;
|
|
154
|
+
this._isPinching = false;
|
|
155
|
+
this._touchInfo = null;
|
|
156
|
+
this._wheelUnusedFactor = 1;
|
|
157
|
+
this._touchUnusedFactor = 1;
|
|
158
|
+
this._pendingPinchFactor = 1;
|
|
159
|
+
this._pinchAC = null;
|
|
151
160
|
this.triggerError = (e) => {
|
|
152
161
|
this.trigger(ERROR, {
|
|
153
162
|
error: e
|
|
@@ -223,6 +232,18 @@ export class PdfViewer extends Component {
|
|
|
223
232
|
}
|
|
224
233
|
e.preventDefault();
|
|
225
234
|
e.stopPropagation();
|
|
235
|
+
if (this.options.pinchToZoom && this._isTrackpadPinch(e)) {
|
|
236
|
+
const scaleFactor = Math.exp(-e.deltaY / 100);
|
|
237
|
+
const newScaleFactor = this._accumulateFactor(this.state.zoomLevel, scaleFactor, '_wheelUnusedFactor');
|
|
238
|
+
if (newScaleFactor === 1) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const zoomLevel = this.state.zoomLevel * newScaleFactor;
|
|
242
|
+
this.triggerZoomStart({ zoomLevel });
|
|
243
|
+
this.zoom({ zoomLevel });
|
|
244
|
+
this.triggerZoomEnd({ zoomLevel });
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
226
247
|
const wheelDelta = mousewheelDelta(e);
|
|
227
248
|
const zoomModifier = wheelDelta < 0 ? 1 : -1;
|
|
228
249
|
const zoomLevel = this.state.zoomLevel + (zoomModifier * this.options.zoomRate);
|
|
@@ -236,6 +257,102 @@ export class PdfViewer extends Component {
|
|
|
236
257
|
zoomLevel: zoomLevel
|
|
237
258
|
});
|
|
238
259
|
};
|
|
260
|
+
this._onKeyDown = (e) => {
|
|
261
|
+
if (e.key === 'Control') {
|
|
262
|
+
this._isCtrlKeyDown = true;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
this._onKeyUp = (e) => {
|
|
266
|
+
if (e.key === 'Control') {
|
|
267
|
+
this._isCtrlKeyDown = false;
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
this._onGestureEvent = (e) => {
|
|
271
|
+
e.preventDefault();
|
|
272
|
+
};
|
|
273
|
+
this._onTouchStart = (e) => {
|
|
274
|
+
var _a;
|
|
275
|
+
if (!this.options.pinchToZoom) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (e.touches.length !== 2) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
// Prevent browser-native pinch-zoom from starting (required for iOS Safari
|
|
282
|
+
// where the gesture decision is made at touchstart time)
|
|
283
|
+
e.preventDefault();
|
|
284
|
+
const touch0 = e.touches[0];
|
|
285
|
+
const touch1 = e.touches[1];
|
|
286
|
+
this._touchInfo = {
|
|
287
|
+
touch0X: touch0.screenX,
|
|
288
|
+
touch0Y: touch0.screenY,
|
|
289
|
+
touch1X: touch1.screenX,
|
|
290
|
+
touch1Y: touch1.screenY
|
|
291
|
+
};
|
|
292
|
+
this._pendingPinchFactor = 1;
|
|
293
|
+
this._touchUnusedFactor = 1;
|
|
294
|
+
const documentContainer = this.getDocumentContainer();
|
|
295
|
+
if (!documentContainer) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const signal = (_a = this._pinchAC) === null || _a === void 0 ? void 0 : _a.signal;
|
|
299
|
+
documentContainer.addEventListener('touchmove', this._onTouchMove, { passive: false, signal });
|
|
300
|
+
documentContainer.addEventListener('touchend', this._onTouchEnd, { signal });
|
|
301
|
+
documentContainer.addEventListener('touchcancel', this._onTouchEnd, { signal });
|
|
302
|
+
};
|
|
303
|
+
this._onTouchMove = (e) => {
|
|
304
|
+
if (!this._touchInfo || e.touches.length !== 2) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
e.preventDefault();
|
|
308
|
+
e.stopPropagation();
|
|
309
|
+
const touch0 = e.touches[0];
|
|
310
|
+
const touch1 = e.touches[1];
|
|
311
|
+
const prevGapX = this._touchInfo.touch1X - this._touchInfo.touch0X;
|
|
312
|
+
const prevGapY = this._touchInfo.touch1Y - this._touchInfo.touch0Y;
|
|
313
|
+
const currGapX = touch1.screenX - touch0.screenX;
|
|
314
|
+
const currGapY = touch1.screenY - touch0.screenY;
|
|
315
|
+
const prevDist = Math.hypot(prevGapX, prevGapY) || 1;
|
|
316
|
+
const currDist = Math.hypot(currGapX, currGapY) || 1;
|
|
317
|
+
if (Math.abs(prevDist - currDist) <= this._minTouchDistanceToPinch) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (!this._isPinching) {
|
|
321
|
+
this._isPinching = true;
|
|
322
|
+
// Suppress panning during pinch
|
|
323
|
+
this.disableScrollerEventsTracking();
|
|
324
|
+
// Fire zoom start on first pinch movement
|
|
325
|
+
this.triggerZoomStart({ zoomLevel: this.state.zoomLevel });
|
|
326
|
+
}
|
|
327
|
+
this._applyPinchTransform(prevDist, currDist);
|
|
328
|
+
// Update stored positions for next move
|
|
329
|
+
this._touchInfo = {
|
|
330
|
+
touch0X: touch0.screenX,
|
|
331
|
+
touch0Y: touch0.screenY,
|
|
332
|
+
touch1X: touch1.screenX,
|
|
333
|
+
touch1Y: touch1.screenY
|
|
334
|
+
};
|
|
335
|
+
};
|
|
336
|
+
this._onTouchEnd = (e) => {
|
|
337
|
+
if (e.touches.length >= 2) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const documentContainer = this.getDocumentContainer();
|
|
341
|
+
if (documentContainer) {
|
|
342
|
+
documentContainer.removeEventListener('touchmove', this._onTouchMove);
|
|
343
|
+
documentContainer.removeEventListener('touchend', this._onTouchEnd);
|
|
344
|
+
documentContainer.removeEventListener('touchcancel', this._onTouchEnd);
|
|
345
|
+
}
|
|
346
|
+
if (this._isPinching) {
|
|
347
|
+
this._clearPinchTransform();
|
|
348
|
+
const zoomLevel = clamp(this.state.zoomLevel * this._pendingPinchFactor, this.options.minZoom, this.options.maxZoom);
|
|
349
|
+
this.zoom({ zoomLevel });
|
|
350
|
+
this.triggerZoomEnd({ zoomLevel });
|
|
351
|
+
// Re-enable panning
|
|
352
|
+
this.enableScrollerEventsTracking();
|
|
353
|
+
}
|
|
354
|
+
this._resetPinchState();
|
|
355
|
+
};
|
|
239
356
|
this.extendOptions(options);
|
|
240
357
|
this.throwIfInvalidOptions();
|
|
241
358
|
this.wrapper = this.element;
|
|
@@ -362,6 +479,7 @@ export class PdfViewer extends Component {
|
|
|
362
479
|
}
|
|
363
480
|
bindEvents() {
|
|
364
481
|
this.bindPagesWheel();
|
|
482
|
+
this.bindPinchToZoomEvents();
|
|
365
483
|
}
|
|
366
484
|
bindPagesWheel() {
|
|
367
485
|
const documentContainer = this.getDocumentContainer();
|
|
@@ -381,6 +499,7 @@ export class PdfViewer extends Component {
|
|
|
381
499
|
}
|
|
382
500
|
unbindEvents() {
|
|
383
501
|
this.unbindPagesWheel();
|
|
502
|
+
this.unbindPinchToZoomEvents();
|
|
384
503
|
}
|
|
385
504
|
unbindPagesWheel() {
|
|
386
505
|
const documentContainer = this.getDocumentContainer();
|
|
@@ -1343,6 +1462,102 @@ export class PdfViewer extends Component {
|
|
|
1343
1462
|
this.element.style.setProperty('--scale-round-x', '1px');
|
|
1344
1463
|
this.element.style.setProperty('--scale-round-y', '1px');
|
|
1345
1464
|
}
|
|
1465
|
+
_isTrackpadPinch(e) {
|
|
1466
|
+
// Trackpad pinch gestures generate wheel events with synthetic ctrlKey=true.
|
|
1467
|
+
// Real Ctrl+scroll has ctrlKey=true but the physical key is down.
|
|
1468
|
+
// Heuristic: ctrlKey is set, physical Ctrl is NOT pressed, pixel-level delta,
|
|
1469
|
+
// deltaX is 0 (no horizontal scroll), and the scale factor is small.
|
|
1470
|
+
if (this._isCtrlKeyDown) {
|
|
1471
|
+
return false;
|
|
1472
|
+
}
|
|
1473
|
+
if (e.deltaMode !== WheelEvent.DOM_DELTA_PIXEL) {
|
|
1474
|
+
return false;
|
|
1475
|
+
}
|
|
1476
|
+
if (e.deltaX !== 0) {
|
|
1477
|
+
return false;
|
|
1478
|
+
}
|
|
1479
|
+
const scaleFactor = Math.exp(-e.deltaY / 100);
|
|
1480
|
+
if (Math.abs(scaleFactor - 1) >= 0.05) {
|
|
1481
|
+
return false;
|
|
1482
|
+
}
|
|
1483
|
+
return true;
|
|
1484
|
+
}
|
|
1485
|
+
_accumulateFactor(previousScale, factor, prop) {
|
|
1486
|
+
if (factor === 1) {
|
|
1487
|
+
return 1;
|
|
1488
|
+
}
|
|
1489
|
+
if ((this[prop] > 1 && factor < 1) || (this[prop] < 1 && factor > 1)) {
|
|
1490
|
+
this[prop] = 1;
|
|
1491
|
+
}
|
|
1492
|
+
const newFactor = Math.floor(previousScale * factor * this[prop] * 100) / (100 * previousScale);
|
|
1493
|
+
this[prop] = factor / (newFactor || factor);
|
|
1494
|
+
return newFactor || 1;
|
|
1495
|
+
}
|
|
1496
|
+
bindPinchToZoomEvents() {
|
|
1497
|
+
if (!this.options.pinchToZoom) {
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
document.addEventListener('keydown', this._onKeyDown);
|
|
1501
|
+
document.addEventListener('keyup', this._onKeyUp);
|
|
1502
|
+
const documentContainer = this.getDocumentContainer();
|
|
1503
|
+
if (!documentContainer) {
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
this._pinchAC = new AbortController();
|
|
1507
|
+
const signal = this._pinchAC.signal;
|
|
1508
|
+
// Prevent the browser from handling pinch-zoom natively
|
|
1509
|
+
documentContainer.style.touchAction = 'none';
|
|
1510
|
+
documentContainer.addEventListener('touchstart', this._onTouchStart, { passive: false, signal });
|
|
1511
|
+
// iOS Safari fires proprietary GestureEvents for pinch gestures.
|
|
1512
|
+
// Suppressing them prevents the browser from zooming the page.
|
|
1513
|
+
documentContainer.addEventListener('gesturestart', this._onGestureEvent, { signal });
|
|
1514
|
+
documentContainer.addEventListener('gesturechange', this._onGestureEvent, { signal });
|
|
1515
|
+
}
|
|
1516
|
+
unbindPinchToZoomEvents() {
|
|
1517
|
+
document.removeEventListener('keydown', this._onKeyDown);
|
|
1518
|
+
document.removeEventListener('keyup', this._onKeyUp);
|
|
1519
|
+
this._isCtrlKeyDown = false;
|
|
1520
|
+
const documentContainer = this.getDocumentContainer();
|
|
1521
|
+
if (documentContainer) {
|
|
1522
|
+
documentContainer.style.touchAction = '';
|
|
1523
|
+
}
|
|
1524
|
+
if (this._pinchAC) {
|
|
1525
|
+
this._pinchAC.abort();
|
|
1526
|
+
this._pinchAC = null;
|
|
1527
|
+
}
|
|
1528
|
+
this._resetPinchState();
|
|
1529
|
+
}
|
|
1530
|
+
_resetPinchState() {
|
|
1531
|
+
this._isPinching = false;
|
|
1532
|
+
this._touchInfo = null;
|
|
1533
|
+
this._touchUnusedFactor = 1;
|
|
1534
|
+
this._wheelUnusedFactor = 1;
|
|
1535
|
+
this._pendingPinchFactor = 1;
|
|
1536
|
+
}
|
|
1537
|
+
get _minTouchDistanceToPinch() {
|
|
1538
|
+
return 32 / (window.devicePixelRatio || 1);
|
|
1539
|
+
}
|
|
1540
|
+
_applyPinchTransform(prevDist, currDist) {
|
|
1541
|
+
const rawFactor = currDist / prevDist;
|
|
1542
|
+
const newFactor = this._accumulateFactor(this.state.zoomLevel, rawFactor, '_touchUnusedFactor');
|
|
1543
|
+
this._pendingPinchFactor *= newFactor;
|
|
1544
|
+
// Clamp the visual factor to the allowed zoom range
|
|
1545
|
+
const targetZoom = this.state.zoomLevel * this._pendingPinchFactor;
|
|
1546
|
+
const clampedZoom = clamp(targetZoom, this.options.minZoom, this.options.maxZoom);
|
|
1547
|
+
const visualFactor = clampedZoom / this.state.zoomLevel;
|
|
1548
|
+
const pagesContainer = this.getPagesContainer();
|
|
1549
|
+
if (pagesContainer) {
|
|
1550
|
+
pagesContainer.style.transform = `scale(${visualFactor})`;
|
|
1551
|
+
pagesContainer.style.transformOrigin = 'center top';
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
_clearPinchTransform() {
|
|
1555
|
+
const pagesContainer = this.getPagesContainer();
|
|
1556
|
+
if (pagesContainer) {
|
|
1557
|
+
pagesContainer.style.transform = '';
|
|
1558
|
+
pagesContainer.style.transformOrigin = '';
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1346
1561
|
activatePageNumber(pageNumber) {
|
|
1347
1562
|
const page = this.getPageByNumber(pageNumber);
|
|
1348
1563
|
if (!page) {
|
|
@@ -47,6 +47,7 @@ export class PdfViewer extends Component {
|
|
|
47
47
|
maxZoom: 4,
|
|
48
48
|
zoomRate: 0.25,
|
|
49
49
|
zoomLevel: DEFAULT_ZOOM_LEVEL,
|
|
50
|
+
pinchToZoom: true,
|
|
50
51
|
zoomBeforePrint: false,
|
|
51
52
|
zoomLevelForPrint: 3,
|
|
52
53
|
renderForms: false,
|
|
@@ -148,6 +149,14 @@ export class PdfViewer extends Component {
|
|
|
148
149
|
this.state = {};
|
|
149
150
|
this.pdfDocument = null;
|
|
150
151
|
this.pages = [];
|
|
152
|
+
// Pinch-to-zoom state
|
|
153
|
+
this._isCtrlKeyDown = false;
|
|
154
|
+
this._isPinching = false;
|
|
155
|
+
this._touchInfo = null;
|
|
156
|
+
this._wheelUnusedFactor = 1;
|
|
157
|
+
this._touchUnusedFactor = 1;
|
|
158
|
+
this._pendingPinchFactor = 1;
|
|
159
|
+
this._pinchAC = null;
|
|
151
160
|
this.triggerError = (e) => {
|
|
152
161
|
this.trigger(ERROR, {
|
|
153
162
|
error: e
|
|
@@ -223,6 +232,18 @@ export class PdfViewer extends Component {
|
|
|
223
232
|
}
|
|
224
233
|
e.preventDefault();
|
|
225
234
|
e.stopPropagation();
|
|
235
|
+
if (this.options.pinchToZoom && this._isTrackpadPinch(e)) {
|
|
236
|
+
const scaleFactor = Math.exp(-e.deltaY / 100);
|
|
237
|
+
const newScaleFactor = this._accumulateFactor(this.state.zoomLevel, scaleFactor, '_wheelUnusedFactor');
|
|
238
|
+
if (newScaleFactor === 1) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const zoomLevel = this.state.zoomLevel * newScaleFactor;
|
|
242
|
+
this.triggerZoomStart({ zoomLevel });
|
|
243
|
+
this.zoom({ zoomLevel });
|
|
244
|
+
this.triggerZoomEnd({ zoomLevel });
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
226
247
|
const wheelDelta = mousewheelDelta(e);
|
|
227
248
|
const zoomModifier = wheelDelta < 0 ? 1 : -1;
|
|
228
249
|
const zoomLevel = this.state.zoomLevel + (zoomModifier * this.options.zoomRate);
|
|
@@ -236,6 +257,102 @@ export class PdfViewer extends Component {
|
|
|
236
257
|
zoomLevel: zoomLevel
|
|
237
258
|
});
|
|
238
259
|
};
|
|
260
|
+
this._onKeyDown = (e) => {
|
|
261
|
+
if (e.key === 'Control') {
|
|
262
|
+
this._isCtrlKeyDown = true;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
this._onKeyUp = (e) => {
|
|
266
|
+
if (e.key === 'Control') {
|
|
267
|
+
this._isCtrlKeyDown = false;
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
this._onGestureEvent = (e) => {
|
|
271
|
+
e.preventDefault();
|
|
272
|
+
};
|
|
273
|
+
this._onTouchStart = (e) => {
|
|
274
|
+
var _a;
|
|
275
|
+
if (!this.options.pinchToZoom) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (e.touches.length !== 2) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
// Prevent browser-native pinch-zoom from starting (required for iOS Safari
|
|
282
|
+
// where the gesture decision is made at touchstart time)
|
|
283
|
+
e.preventDefault();
|
|
284
|
+
const touch0 = e.touches[0];
|
|
285
|
+
const touch1 = e.touches[1];
|
|
286
|
+
this._touchInfo = {
|
|
287
|
+
touch0X: touch0.screenX,
|
|
288
|
+
touch0Y: touch0.screenY,
|
|
289
|
+
touch1X: touch1.screenX,
|
|
290
|
+
touch1Y: touch1.screenY
|
|
291
|
+
};
|
|
292
|
+
this._pendingPinchFactor = 1;
|
|
293
|
+
this._touchUnusedFactor = 1;
|
|
294
|
+
const documentContainer = this.getDocumentContainer();
|
|
295
|
+
if (!documentContainer) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const signal = (_a = this._pinchAC) === null || _a === void 0 ? void 0 : _a.signal;
|
|
299
|
+
documentContainer.addEventListener('touchmove', this._onTouchMove, { passive: false, signal });
|
|
300
|
+
documentContainer.addEventListener('touchend', this._onTouchEnd, { signal });
|
|
301
|
+
documentContainer.addEventListener('touchcancel', this._onTouchEnd, { signal });
|
|
302
|
+
};
|
|
303
|
+
this._onTouchMove = (e) => {
|
|
304
|
+
if (!this._touchInfo || e.touches.length !== 2) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
e.preventDefault();
|
|
308
|
+
e.stopPropagation();
|
|
309
|
+
const touch0 = e.touches[0];
|
|
310
|
+
const touch1 = e.touches[1];
|
|
311
|
+
const prevGapX = this._touchInfo.touch1X - this._touchInfo.touch0X;
|
|
312
|
+
const prevGapY = this._touchInfo.touch1Y - this._touchInfo.touch0Y;
|
|
313
|
+
const currGapX = touch1.screenX - touch0.screenX;
|
|
314
|
+
const currGapY = touch1.screenY - touch0.screenY;
|
|
315
|
+
const prevDist = Math.hypot(prevGapX, prevGapY) || 1;
|
|
316
|
+
const currDist = Math.hypot(currGapX, currGapY) || 1;
|
|
317
|
+
if (Math.abs(prevDist - currDist) <= this._minTouchDistanceToPinch) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (!this._isPinching) {
|
|
321
|
+
this._isPinching = true;
|
|
322
|
+
// Suppress panning during pinch
|
|
323
|
+
this.disableScrollerEventsTracking();
|
|
324
|
+
// Fire zoom start on first pinch movement
|
|
325
|
+
this.triggerZoomStart({ zoomLevel: this.state.zoomLevel });
|
|
326
|
+
}
|
|
327
|
+
this._applyPinchTransform(prevDist, currDist);
|
|
328
|
+
// Update stored positions for next move
|
|
329
|
+
this._touchInfo = {
|
|
330
|
+
touch0X: touch0.screenX,
|
|
331
|
+
touch0Y: touch0.screenY,
|
|
332
|
+
touch1X: touch1.screenX,
|
|
333
|
+
touch1Y: touch1.screenY
|
|
334
|
+
};
|
|
335
|
+
};
|
|
336
|
+
this._onTouchEnd = (e) => {
|
|
337
|
+
if (e.touches.length >= 2) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const documentContainer = this.getDocumentContainer();
|
|
341
|
+
if (documentContainer) {
|
|
342
|
+
documentContainer.removeEventListener('touchmove', this._onTouchMove);
|
|
343
|
+
documentContainer.removeEventListener('touchend', this._onTouchEnd);
|
|
344
|
+
documentContainer.removeEventListener('touchcancel', this._onTouchEnd);
|
|
345
|
+
}
|
|
346
|
+
if (this._isPinching) {
|
|
347
|
+
this._clearPinchTransform();
|
|
348
|
+
const zoomLevel = clamp(this.state.zoomLevel * this._pendingPinchFactor, this.options.minZoom, this.options.maxZoom);
|
|
349
|
+
this.zoom({ zoomLevel });
|
|
350
|
+
this.triggerZoomEnd({ zoomLevel });
|
|
351
|
+
// Re-enable panning
|
|
352
|
+
this.enableScrollerEventsTracking();
|
|
353
|
+
}
|
|
354
|
+
this._resetPinchState();
|
|
355
|
+
};
|
|
239
356
|
this.extendOptions(options);
|
|
240
357
|
this.throwIfInvalidOptions();
|
|
241
358
|
this.wrapper = this.element;
|
|
@@ -362,6 +479,7 @@ export class PdfViewer extends Component {
|
|
|
362
479
|
}
|
|
363
480
|
bindEvents() {
|
|
364
481
|
this.bindPagesWheel();
|
|
482
|
+
this.bindPinchToZoomEvents();
|
|
365
483
|
}
|
|
366
484
|
bindPagesWheel() {
|
|
367
485
|
const documentContainer = this.getDocumentContainer();
|
|
@@ -381,6 +499,7 @@ export class PdfViewer extends Component {
|
|
|
381
499
|
}
|
|
382
500
|
unbindEvents() {
|
|
383
501
|
this.unbindPagesWheel();
|
|
502
|
+
this.unbindPinchToZoomEvents();
|
|
384
503
|
}
|
|
385
504
|
unbindPagesWheel() {
|
|
386
505
|
const documentContainer = this.getDocumentContainer();
|
|
@@ -1343,6 +1462,102 @@ export class PdfViewer extends Component {
|
|
|
1343
1462
|
this.element.style.setProperty('--scale-round-x', '1px');
|
|
1344
1463
|
this.element.style.setProperty('--scale-round-y', '1px');
|
|
1345
1464
|
}
|
|
1465
|
+
_isTrackpadPinch(e) {
|
|
1466
|
+
// Trackpad pinch gestures generate wheel events with synthetic ctrlKey=true.
|
|
1467
|
+
// Real Ctrl+scroll has ctrlKey=true but the physical key is down.
|
|
1468
|
+
// Heuristic: ctrlKey is set, physical Ctrl is NOT pressed, pixel-level delta,
|
|
1469
|
+
// deltaX is 0 (no horizontal scroll), and the scale factor is small.
|
|
1470
|
+
if (this._isCtrlKeyDown) {
|
|
1471
|
+
return false;
|
|
1472
|
+
}
|
|
1473
|
+
if (e.deltaMode !== WheelEvent.DOM_DELTA_PIXEL) {
|
|
1474
|
+
return false;
|
|
1475
|
+
}
|
|
1476
|
+
if (e.deltaX !== 0) {
|
|
1477
|
+
return false;
|
|
1478
|
+
}
|
|
1479
|
+
const scaleFactor = Math.exp(-e.deltaY / 100);
|
|
1480
|
+
if (Math.abs(scaleFactor - 1) >= 0.05) {
|
|
1481
|
+
return false;
|
|
1482
|
+
}
|
|
1483
|
+
return true;
|
|
1484
|
+
}
|
|
1485
|
+
_accumulateFactor(previousScale, factor, prop) {
|
|
1486
|
+
if (factor === 1) {
|
|
1487
|
+
return 1;
|
|
1488
|
+
}
|
|
1489
|
+
if ((this[prop] > 1 && factor < 1) || (this[prop] < 1 && factor > 1)) {
|
|
1490
|
+
this[prop] = 1;
|
|
1491
|
+
}
|
|
1492
|
+
const newFactor = Math.floor(previousScale * factor * this[prop] * 100) / (100 * previousScale);
|
|
1493
|
+
this[prop] = factor / (newFactor || factor);
|
|
1494
|
+
return newFactor || 1;
|
|
1495
|
+
}
|
|
1496
|
+
bindPinchToZoomEvents() {
|
|
1497
|
+
if (!this.options.pinchToZoom) {
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
document.addEventListener('keydown', this._onKeyDown);
|
|
1501
|
+
document.addEventListener('keyup', this._onKeyUp);
|
|
1502
|
+
const documentContainer = this.getDocumentContainer();
|
|
1503
|
+
if (!documentContainer) {
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
this._pinchAC = new AbortController();
|
|
1507
|
+
const signal = this._pinchAC.signal;
|
|
1508
|
+
// Prevent the browser from handling pinch-zoom natively
|
|
1509
|
+
documentContainer.style.touchAction = 'none';
|
|
1510
|
+
documentContainer.addEventListener('touchstart', this._onTouchStart, { passive: false, signal });
|
|
1511
|
+
// iOS Safari fires proprietary GestureEvents for pinch gestures.
|
|
1512
|
+
// Suppressing them prevents the browser from zooming the page.
|
|
1513
|
+
documentContainer.addEventListener('gesturestart', this._onGestureEvent, { signal });
|
|
1514
|
+
documentContainer.addEventListener('gesturechange', this._onGestureEvent, { signal });
|
|
1515
|
+
}
|
|
1516
|
+
unbindPinchToZoomEvents() {
|
|
1517
|
+
document.removeEventListener('keydown', this._onKeyDown);
|
|
1518
|
+
document.removeEventListener('keyup', this._onKeyUp);
|
|
1519
|
+
this._isCtrlKeyDown = false;
|
|
1520
|
+
const documentContainer = this.getDocumentContainer();
|
|
1521
|
+
if (documentContainer) {
|
|
1522
|
+
documentContainer.style.touchAction = '';
|
|
1523
|
+
}
|
|
1524
|
+
if (this._pinchAC) {
|
|
1525
|
+
this._pinchAC.abort();
|
|
1526
|
+
this._pinchAC = null;
|
|
1527
|
+
}
|
|
1528
|
+
this._resetPinchState();
|
|
1529
|
+
}
|
|
1530
|
+
_resetPinchState() {
|
|
1531
|
+
this._isPinching = false;
|
|
1532
|
+
this._touchInfo = null;
|
|
1533
|
+
this._touchUnusedFactor = 1;
|
|
1534
|
+
this._wheelUnusedFactor = 1;
|
|
1535
|
+
this._pendingPinchFactor = 1;
|
|
1536
|
+
}
|
|
1537
|
+
get _minTouchDistanceToPinch() {
|
|
1538
|
+
return 32 / (window.devicePixelRatio || 1);
|
|
1539
|
+
}
|
|
1540
|
+
_applyPinchTransform(prevDist, currDist) {
|
|
1541
|
+
const rawFactor = currDist / prevDist;
|
|
1542
|
+
const newFactor = this._accumulateFactor(this.state.zoomLevel, rawFactor, '_touchUnusedFactor');
|
|
1543
|
+
this._pendingPinchFactor *= newFactor;
|
|
1544
|
+
// Clamp the visual factor to the allowed zoom range
|
|
1545
|
+
const targetZoom = this.state.zoomLevel * this._pendingPinchFactor;
|
|
1546
|
+
const clampedZoom = clamp(targetZoom, this.options.minZoom, this.options.maxZoom);
|
|
1547
|
+
const visualFactor = clampedZoom / this.state.zoomLevel;
|
|
1548
|
+
const pagesContainer = this.getPagesContainer();
|
|
1549
|
+
if (pagesContainer) {
|
|
1550
|
+
pagesContainer.style.transform = `scale(${visualFactor})`;
|
|
1551
|
+
pagesContainer.style.transformOrigin = 'center top';
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
_clearPinchTransform() {
|
|
1555
|
+
const pagesContainer = this.getPagesContainer();
|
|
1556
|
+
if (pagesContainer) {
|
|
1557
|
+
pagesContainer.style.transform = '';
|
|
1558
|
+
pagesContainer.style.transformOrigin = '';
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1346
1561
|
activatePageNumber(pageNumber) {
|
|
1347
1562
|
const page = this.getPageByNumber(pageNumber);
|
|
1348
1563
|
if (!page) {
|
|
@@ -33,6 +33,18 @@ export declare class PdfViewer extends Component {
|
|
|
33
33
|
searchService: SearchService;
|
|
34
34
|
shouldPreventScroll: boolean;
|
|
35
35
|
eventBus: EventBus;
|
|
36
|
+
_isCtrlKeyDown: boolean;
|
|
37
|
+
_isPinching: boolean;
|
|
38
|
+
_touchInfo: {
|
|
39
|
+
touch0X: number;
|
|
40
|
+
touch0Y: number;
|
|
41
|
+
touch1X: number;
|
|
42
|
+
touch1Y: number;
|
|
43
|
+
} | null;
|
|
44
|
+
_wheelUnusedFactor: number;
|
|
45
|
+
_touchUnusedFactor: number;
|
|
46
|
+
_pendingPinchFactor: number;
|
|
47
|
+
_pinchAC: AbortController | null;
|
|
36
48
|
constructor(element: any, options: any);
|
|
37
49
|
destroy(): void;
|
|
38
50
|
throwIfInvalidOptions(): void;
|
|
@@ -183,6 +195,20 @@ export declare class PdfViewer extends Component {
|
|
|
183
195
|
disableScrollerEventsTracking(): void;
|
|
184
196
|
setScaleFactor(scaleFactor: number): void;
|
|
185
197
|
onDocumentWheel: (e: any) => void;
|
|
198
|
+
_isTrackpadPinch(e: WheelEvent): boolean;
|
|
199
|
+
_accumulateFactor(previousScale: number, factor: number, prop: '_wheelUnusedFactor' | '_touchUnusedFactor'): number;
|
|
200
|
+
_onKeyDown: (e: KeyboardEvent) => void;
|
|
201
|
+
_onKeyUp: (e: KeyboardEvent) => void;
|
|
202
|
+
bindPinchToZoomEvents(): void;
|
|
203
|
+
unbindPinchToZoomEvents(): void;
|
|
204
|
+
_resetPinchState(): void;
|
|
205
|
+
get _minTouchDistanceToPinch(): number;
|
|
206
|
+
_onGestureEvent: (e: Event) => void;
|
|
207
|
+
_onTouchStart: (e: TouchEvent) => void;
|
|
208
|
+
_onTouchMove: (e: TouchEvent) => void;
|
|
209
|
+
_onTouchEnd: (e: TouchEvent) => void;
|
|
210
|
+
_applyPinchTransform(prevDist: number, currDist: number): void;
|
|
211
|
+
_clearPinchTransform(): void;
|
|
186
212
|
activatePageNumber(pageNumber: any): void;
|
|
187
213
|
scrollToPage({ pageNumber }: {
|
|
188
214
|
pageNumber: any;
|
|
@@ -50,6 +50,7 @@ class PdfViewer extends main_1.Component {
|
|
|
50
50
|
maxZoom: 4,
|
|
51
51
|
zoomRate: 0.25,
|
|
52
52
|
zoomLevel: DEFAULT_ZOOM_LEVEL,
|
|
53
|
+
pinchToZoom: true,
|
|
53
54
|
zoomBeforePrint: false,
|
|
54
55
|
zoomLevelForPrint: 3,
|
|
55
56
|
renderForms: false,
|
|
@@ -151,6 +152,14 @@ class PdfViewer extends main_1.Component {
|
|
|
151
152
|
this.state = {};
|
|
152
153
|
this.pdfDocument = null;
|
|
153
154
|
this.pages = [];
|
|
155
|
+
// Pinch-to-zoom state
|
|
156
|
+
this._isCtrlKeyDown = false;
|
|
157
|
+
this._isPinching = false;
|
|
158
|
+
this._touchInfo = null;
|
|
159
|
+
this._wheelUnusedFactor = 1;
|
|
160
|
+
this._touchUnusedFactor = 1;
|
|
161
|
+
this._pendingPinchFactor = 1;
|
|
162
|
+
this._pinchAC = null;
|
|
154
163
|
this.triggerError = (e) => {
|
|
155
164
|
this.trigger(ERROR, {
|
|
156
165
|
error: e
|
|
@@ -226,6 +235,18 @@ class PdfViewer extends main_1.Component {
|
|
|
226
235
|
}
|
|
227
236
|
e.preventDefault();
|
|
228
237
|
e.stopPropagation();
|
|
238
|
+
if (this.options.pinchToZoom && this._isTrackpadPinch(e)) {
|
|
239
|
+
const scaleFactor = Math.exp(-e.deltaY / 100);
|
|
240
|
+
const newScaleFactor = this._accumulateFactor(this.state.zoomLevel, scaleFactor, '_wheelUnusedFactor');
|
|
241
|
+
if (newScaleFactor === 1) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const zoomLevel = this.state.zoomLevel * newScaleFactor;
|
|
245
|
+
this.triggerZoomStart({ zoomLevel });
|
|
246
|
+
this.zoom({ zoomLevel });
|
|
247
|
+
this.triggerZoomEnd({ zoomLevel });
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
229
250
|
const wheelDelta = (0, main_1.mousewheelDelta)(e);
|
|
230
251
|
const zoomModifier = wheelDelta < 0 ? 1 : -1;
|
|
231
252
|
const zoomLevel = this.state.zoomLevel + (zoomModifier * this.options.zoomRate);
|
|
@@ -239,6 +260,102 @@ class PdfViewer extends main_1.Component {
|
|
|
239
260
|
zoomLevel: zoomLevel
|
|
240
261
|
});
|
|
241
262
|
};
|
|
263
|
+
this._onKeyDown = (e) => {
|
|
264
|
+
if (e.key === 'Control') {
|
|
265
|
+
this._isCtrlKeyDown = true;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
this._onKeyUp = (e) => {
|
|
269
|
+
if (e.key === 'Control') {
|
|
270
|
+
this._isCtrlKeyDown = false;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
this._onGestureEvent = (e) => {
|
|
274
|
+
e.preventDefault();
|
|
275
|
+
};
|
|
276
|
+
this._onTouchStart = (e) => {
|
|
277
|
+
var _a;
|
|
278
|
+
if (!this.options.pinchToZoom) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (e.touches.length !== 2) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
// Prevent browser-native pinch-zoom from starting (required for iOS Safari
|
|
285
|
+
// where the gesture decision is made at touchstart time)
|
|
286
|
+
e.preventDefault();
|
|
287
|
+
const touch0 = e.touches[0];
|
|
288
|
+
const touch1 = e.touches[1];
|
|
289
|
+
this._touchInfo = {
|
|
290
|
+
touch0X: touch0.screenX,
|
|
291
|
+
touch0Y: touch0.screenY,
|
|
292
|
+
touch1X: touch1.screenX,
|
|
293
|
+
touch1Y: touch1.screenY
|
|
294
|
+
};
|
|
295
|
+
this._pendingPinchFactor = 1;
|
|
296
|
+
this._touchUnusedFactor = 1;
|
|
297
|
+
const documentContainer = this.getDocumentContainer();
|
|
298
|
+
if (!documentContainer) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const signal = (_a = this._pinchAC) === null || _a === void 0 ? void 0 : _a.signal;
|
|
302
|
+
documentContainer.addEventListener('touchmove', this._onTouchMove, { passive: false, signal });
|
|
303
|
+
documentContainer.addEventListener('touchend', this._onTouchEnd, { signal });
|
|
304
|
+
documentContainer.addEventListener('touchcancel', this._onTouchEnd, { signal });
|
|
305
|
+
};
|
|
306
|
+
this._onTouchMove = (e) => {
|
|
307
|
+
if (!this._touchInfo || e.touches.length !== 2) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
e.preventDefault();
|
|
311
|
+
e.stopPropagation();
|
|
312
|
+
const touch0 = e.touches[0];
|
|
313
|
+
const touch1 = e.touches[1];
|
|
314
|
+
const prevGapX = this._touchInfo.touch1X - this._touchInfo.touch0X;
|
|
315
|
+
const prevGapY = this._touchInfo.touch1Y - this._touchInfo.touch0Y;
|
|
316
|
+
const currGapX = touch1.screenX - touch0.screenX;
|
|
317
|
+
const currGapY = touch1.screenY - touch0.screenY;
|
|
318
|
+
const prevDist = Math.hypot(prevGapX, prevGapY) || 1;
|
|
319
|
+
const currDist = Math.hypot(currGapX, currGapY) || 1;
|
|
320
|
+
if (Math.abs(prevDist - currDist) <= this._minTouchDistanceToPinch) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (!this._isPinching) {
|
|
324
|
+
this._isPinching = true;
|
|
325
|
+
// Suppress panning during pinch
|
|
326
|
+
this.disableScrollerEventsTracking();
|
|
327
|
+
// Fire zoom start on first pinch movement
|
|
328
|
+
this.triggerZoomStart({ zoomLevel: this.state.zoomLevel });
|
|
329
|
+
}
|
|
330
|
+
this._applyPinchTransform(prevDist, currDist);
|
|
331
|
+
// Update stored positions for next move
|
|
332
|
+
this._touchInfo = {
|
|
333
|
+
touch0X: touch0.screenX,
|
|
334
|
+
touch0Y: touch0.screenY,
|
|
335
|
+
touch1X: touch1.screenX,
|
|
336
|
+
touch1Y: touch1.screenY
|
|
337
|
+
};
|
|
338
|
+
};
|
|
339
|
+
this._onTouchEnd = (e) => {
|
|
340
|
+
if (e.touches.length >= 2) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const documentContainer = this.getDocumentContainer();
|
|
344
|
+
if (documentContainer) {
|
|
345
|
+
documentContainer.removeEventListener('touchmove', this._onTouchMove);
|
|
346
|
+
documentContainer.removeEventListener('touchend', this._onTouchEnd);
|
|
347
|
+
documentContainer.removeEventListener('touchcancel', this._onTouchEnd);
|
|
348
|
+
}
|
|
349
|
+
if (this._isPinching) {
|
|
350
|
+
this._clearPinchTransform();
|
|
351
|
+
const zoomLevel = (0, main_1.clamp)(this.state.zoomLevel * this._pendingPinchFactor, this.options.minZoom, this.options.maxZoom);
|
|
352
|
+
this.zoom({ zoomLevel });
|
|
353
|
+
this.triggerZoomEnd({ zoomLevel });
|
|
354
|
+
// Re-enable panning
|
|
355
|
+
this.enableScrollerEventsTracking();
|
|
356
|
+
}
|
|
357
|
+
this._resetPinchState();
|
|
358
|
+
};
|
|
242
359
|
this.extendOptions(options);
|
|
243
360
|
this.throwIfInvalidOptions();
|
|
244
361
|
this.wrapper = this.element;
|
|
@@ -365,6 +482,7 @@ class PdfViewer extends main_1.Component {
|
|
|
365
482
|
}
|
|
366
483
|
bindEvents() {
|
|
367
484
|
this.bindPagesWheel();
|
|
485
|
+
this.bindPinchToZoomEvents();
|
|
368
486
|
}
|
|
369
487
|
bindPagesWheel() {
|
|
370
488
|
const documentContainer = this.getDocumentContainer();
|
|
@@ -384,6 +502,7 @@ class PdfViewer extends main_1.Component {
|
|
|
384
502
|
}
|
|
385
503
|
unbindEvents() {
|
|
386
504
|
this.unbindPagesWheel();
|
|
505
|
+
this.unbindPinchToZoomEvents();
|
|
387
506
|
}
|
|
388
507
|
unbindPagesWheel() {
|
|
389
508
|
const documentContainer = this.getDocumentContainer();
|
|
@@ -1346,6 +1465,102 @@ class PdfViewer extends main_1.Component {
|
|
|
1346
1465
|
this.element.style.setProperty('--scale-round-x', '1px');
|
|
1347
1466
|
this.element.style.setProperty('--scale-round-y', '1px');
|
|
1348
1467
|
}
|
|
1468
|
+
_isTrackpadPinch(e) {
|
|
1469
|
+
// Trackpad pinch gestures generate wheel events with synthetic ctrlKey=true.
|
|
1470
|
+
// Real Ctrl+scroll has ctrlKey=true but the physical key is down.
|
|
1471
|
+
// Heuristic: ctrlKey is set, physical Ctrl is NOT pressed, pixel-level delta,
|
|
1472
|
+
// deltaX is 0 (no horizontal scroll), and the scale factor is small.
|
|
1473
|
+
if (this._isCtrlKeyDown) {
|
|
1474
|
+
return false;
|
|
1475
|
+
}
|
|
1476
|
+
if (e.deltaMode !== WheelEvent.DOM_DELTA_PIXEL) {
|
|
1477
|
+
return false;
|
|
1478
|
+
}
|
|
1479
|
+
if (e.deltaX !== 0) {
|
|
1480
|
+
return false;
|
|
1481
|
+
}
|
|
1482
|
+
const scaleFactor = Math.exp(-e.deltaY / 100);
|
|
1483
|
+
if (Math.abs(scaleFactor - 1) >= 0.05) {
|
|
1484
|
+
return false;
|
|
1485
|
+
}
|
|
1486
|
+
return true;
|
|
1487
|
+
}
|
|
1488
|
+
_accumulateFactor(previousScale, factor, prop) {
|
|
1489
|
+
if (factor === 1) {
|
|
1490
|
+
return 1;
|
|
1491
|
+
}
|
|
1492
|
+
if ((this[prop] > 1 && factor < 1) || (this[prop] < 1 && factor > 1)) {
|
|
1493
|
+
this[prop] = 1;
|
|
1494
|
+
}
|
|
1495
|
+
const newFactor = Math.floor(previousScale * factor * this[prop] * 100) / (100 * previousScale);
|
|
1496
|
+
this[prop] = factor / (newFactor || factor);
|
|
1497
|
+
return newFactor || 1;
|
|
1498
|
+
}
|
|
1499
|
+
bindPinchToZoomEvents() {
|
|
1500
|
+
if (!this.options.pinchToZoom) {
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1503
|
+
document.addEventListener('keydown', this._onKeyDown);
|
|
1504
|
+
document.addEventListener('keyup', this._onKeyUp);
|
|
1505
|
+
const documentContainer = this.getDocumentContainer();
|
|
1506
|
+
if (!documentContainer) {
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
this._pinchAC = new AbortController();
|
|
1510
|
+
const signal = this._pinchAC.signal;
|
|
1511
|
+
// Prevent the browser from handling pinch-zoom natively
|
|
1512
|
+
documentContainer.style.touchAction = 'none';
|
|
1513
|
+
documentContainer.addEventListener('touchstart', this._onTouchStart, { passive: false, signal });
|
|
1514
|
+
// iOS Safari fires proprietary GestureEvents for pinch gestures.
|
|
1515
|
+
// Suppressing them prevents the browser from zooming the page.
|
|
1516
|
+
documentContainer.addEventListener('gesturestart', this._onGestureEvent, { signal });
|
|
1517
|
+
documentContainer.addEventListener('gesturechange', this._onGestureEvent, { signal });
|
|
1518
|
+
}
|
|
1519
|
+
unbindPinchToZoomEvents() {
|
|
1520
|
+
document.removeEventListener('keydown', this._onKeyDown);
|
|
1521
|
+
document.removeEventListener('keyup', this._onKeyUp);
|
|
1522
|
+
this._isCtrlKeyDown = false;
|
|
1523
|
+
const documentContainer = this.getDocumentContainer();
|
|
1524
|
+
if (documentContainer) {
|
|
1525
|
+
documentContainer.style.touchAction = '';
|
|
1526
|
+
}
|
|
1527
|
+
if (this._pinchAC) {
|
|
1528
|
+
this._pinchAC.abort();
|
|
1529
|
+
this._pinchAC = null;
|
|
1530
|
+
}
|
|
1531
|
+
this._resetPinchState();
|
|
1532
|
+
}
|
|
1533
|
+
_resetPinchState() {
|
|
1534
|
+
this._isPinching = false;
|
|
1535
|
+
this._touchInfo = null;
|
|
1536
|
+
this._touchUnusedFactor = 1;
|
|
1537
|
+
this._wheelUnusedFactor = 1;
|
|
1538
|
+
this._pendingPinchFactor = 1;
|
|
1539
|
+
}
|
|
1540
|
+
get _minTouchDistanceToPinch() {
|
|
1541
|
+
return 32 / (window.devicePixelRatio || 1);
|
|
1542
|
+
}
|
|
1543
|
+
_applyPinchTransform(prevDist, currDist) {
|
|
1544
|
+
const rawFactor = currDist / prevDist;
|
|
1545
|
+
const newFactor = this._accumulateFactor(this.state.zoomLevel, rawFactor, '_touchUnusedFactor');
|
|
1546
|
+
this._pendingPinchFactor *= newFactor;
|
|
1547
|
+
// Clamp the visual factor to the allowed zoom range
|
|
1548
|
+
const targetZoom = this.state.zoomLevel * this._pendingPinchFactor;
|
|
1549
|
+
const clampedZoom = (0, main_1.clamp)(targetZoom, this.options.minZoom, this.options.maxZoom);
|
|
1550
|
+
const visualFactor = clampedZoom / this.state.zoomLevel;
|
|
1551
|
+
const pagesContainer = this.getPagesContainer();
|
|
1552
|
+
if (pagesContainer) {
|
|
1553
|
+
pagesContainer.style.transform = `scale(${visualFactor})`;
|
|
1554
|
+
pagesContainer.style.transformOrigin = 'center top';
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
_clearPinchTransform() {
|
|
1558
|
+
const pagesContainer = this.getPagesContainer();
|
|
1559
|
+
if (pagesContainer) {
|
|
1560
|
+
pagesContainer.style.transform = '';
|
|
1561
|
+
pagesContainer.style.transformOrigin = '';
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1349
1564
|
activatePageNumber(pageNumber) {
|
|
1350
1565
|
const page = this.getPageByNumber(pageNumber);
|
|
1351
1566
|
if (!page) {
|
package/package.json
CHANGED