feat(PdfViewer): 添加双指缩放手势支持
实现PDF查看器的双指缩放功能,包括触摸开始、移动和结束的完整手势处理
Showing
1 changed file
with
106 additions
and
7 deletions
| ... | @@ -536,18 +536,117 @@ const addPreventSaveListeners = () => { | ... | @@ -536,18 +536,117 @@ const addPreventSaveListeners = () => { |
| 536 | return false; | 536 | return false; |
| 537 | }); | 537 | }); |
| 538 | 538 | ||
| 539 | - // 防止触摸回调(移动端长按) | 539 | + // 添加双指缩放手势支持 |
| 540 | - pdfContainer.addEventListener('touchstart', (e) => { | 540 | + addPinchZoomListeners(pdfContainer); |
| 541 | - if (e.touches.length > 1) { | 541 | + } |
| 542 | +}; | ||
| 543 | + | ||
| 544 | +// 双指缩放相关变量 | ||
| 545 | +let initialDistance = 0; | ||
| 546 | +let initialZoom = 1; | ||
| 547 | +let isZooming = false; | ||
| 548 | + | ||
| 549 | +/** | ||
| 550 | + * 添加双指缩放手势监听器 | ||
| 551 | + */ | ||
| 552 | +const addPinchZoomListeners = (container) => { | ||
| 553 | + let touches = []; | ||
| 554 | + | ||
| 555 | + // 计算两点间距离 | ||
| 556 | + const getDistance = (touch1, touch2) => { | ||
| 557 | + const dx = touch1.clientX - touch2.clientX; | ||
| 558 | + const dy = touch1.clientY - touch2.clientY; | ||
| 559 | + return Math.sqrt(dx * dx + dy * dy); | ||
| 560 | + }; | ||
| 561 | + | ||
| 562 | + // 获取两点中心位置 | ||
| 563 | + const getCenter = (touch1, touch2) => { | ||
| 564 | + return { | ||
| 565 | + x: (touch1.clientX + touch2.clientX) / 2, | ||
| 566 | + y: (touch1.clientY + touch2.clientY) / 2 | ||
| 567 | + }; | ||
| 568 | + }; | ||
| 569 | + | ||
| 570 | + // 触摸开始 | ||
| 571 | + container.addEventListener('touchstart', (e) => { | ||
| 572 | + touches = Array.from(e.touches); | ||
| 573 | + | ||
| 574 | + if (touches.length === 2) { | ||
| 575 | + // 双指触摸开始 | ||
| 576 | + e.preventDefault(); | ||
| 577 | + isZooming = true; | ||
| 578 | + initialDistance = getDistance(touches[0], touches[1]); | ||
| 579 | + initialZoom = zoomLevel.value; | ||
| 580 | + } else if (touches.length > 2) { | ||
| 581 | + // 超过两指时阻止默认行为 | ||
| 542 | e.preventDefault(); | 582 | e.preventDefault(); |
| 543 | } | 583 | } |
| 544 | - }); | 584 | + }, { passive: false }); |
| 545 | 585 | ||
| 546 | - // 防止双指缩放等手势 | 586 | + // 触摸移动 |
| 547 | - pdfContainer.addEventListener('gesturestart', (e) => { | 587 | + container.addEventListener('touchmove', (e) => { |
| 588 | + if (e.touches.length === 2 && isZooming) { | ||
| 548 | e.preventDefault(); | 589 | e.preventDefault(); |
| 549 | - }); | 590 | + |
| 591 | + const currentTouches = Array.from(e.touches); | ||
| 592 | + const currentDistance = getDistance(currentTouches[0], currentTouches[1]); | ||
| 593 | + const scale = currentDistance / initialDistance; | ||
| 594 | + | ||
| 595 | + // 计算新的缩放级别 | ||
| 596 | + let newZoom = initialZoom * scale; | ||
| 597 | + newZoom = Math.max(0.5, Math.min(3, newZoom)); // 限制缩放范围 | ||
| 598 | + | ||
| 599 | + // 获取缩放中心点 | ||
| 600 | + const center = getCenter(currentTouches[0], currentTouches[1]); | ||
| 601 | + const rect = container.getBoundingClientRect(); | ||
| 602 | + const centerX = center.x - rect.left; | ||
| 603 | + const centerY = center.y - rect.top; | ||
| 604 | + | ||
| 605 | + // 应用缩放 | ||
| 606 | + applyPinchZoom(newZoom, centerX, centerY, container); | ||
| 550 | } | 607 | } |
| 608 | + }, { passive: false }); | ||
| 609 | + | ||
| 610 | + // 触摸结束 | ||
| 611 | + container.addEventListener('touchend', (e) => { | ||
| 612 | + if (isZooming && e.touches.length < 2) { | ||
| 613 | + isZooming = false; | ||
| 614 | + initialDistance = 0; | ||
| 615 | + initialZoom = zoomLevel.value; | ||
| 616 | + } | ||
| 617 | + }); | ||
| 618 | + | ||
| 619 | + // 触摸取消 | ||
| 620 | + container.addEventListener('touchcancel', (e) => { | ||
| 621 | + isZooming = false; | ||
| 622 | + initialDistance = 0; | ||
| 623 | + initialZoom = zoomLevel.value; | ||
| 624 | + }); | ||
| 625 | +}; | ||
| 626 | + | ||
| 627 | +/** | ||
| 628 | + * 应用双指缩放 | ||
| 629 | + */ | ||
| 630 | +const applyPinchZoom = (newZoom, centerX, centerY, container) => { | ||
| 631 | + const oldZoom = zoomLevel.value; | ||
| 632 | + const oldScrollLeft = container.scrollLeft; | ||
| 633 | + const oldScrollTop = container.scrollTop; | ||
| 634 | + | ||
| 635 | + // 更新缩放级别 | ||
| 636 | + zoomLevel.value = newZoom; | ||
| 637 | + | ||
| 638 | + // 等待DOM更新后调整滚动位置 | ||
| 639 | + nextTick(() => { | ||
| 640 | + const zoomRatio = newZoom / oldZoom; | ||
| 641 | + | ||
| 642 | + // 计算新的滚动位置,以缩放中心为锚点 | ||
| 643 | + const newScrollLeft = (oldScrollLeft + centerX) * zoomRatio - centerX; | ||
| 644 | + const newScrollTop = (oldScrollTop + centerY) * zoomRatio - centerY; | ||
| 645 | + | ||
| 646 | + // 应用新的滚动位置 | ||
| 647 | + container.scrollLeft = Math.max(0, newScrollLeft); | ||
| 648 | + container.scrollTop = Math.max(0, newScrollTop); | ||
| 649 | + }); | ||
| 551 | }; | 650 | }; |
| 552 | </script> | 651 | </script> |
| 553 | 652 | ... | ... |
-
Please register or login to post a comment