fix(PdfViewer): 改进缩放功能以保持视图中心位置
重构缩放逻辑,使用比例计算代替直接缩放比率,确保缩放时视图中心位置保持不变 移除平滑滚动以避免偏移抖动,直接设置滚动位置
Showing
1 changed file
with
55 additions
and
37 deletions
| ... | @@ -354,28 +354,40 @@ const handleClose = () => { | ... | @@ -354,28 +354,40 @@ const handleClose = () => { |
| 354 | */ | 354 | */ |
| 355 | const zoomIn = () => { | 355 | const zoomIn = () => { |
| 356 | if (zoomLevel.value < 3) { // 最大放大到300% | 356 | if (zoomLevel.value < 3) { // 最大放大到300% |
| 357 | - // 记录缩放前的滚动位置 | ||
| 358 | const pdfContainer = document.querySelector('.pdf-viewer-container .pdf-content'); | 357 | const pdfContainer = document.querySelector('.pdf-viewer-container .pdf-content'); |
| 359 | if (pdfContainer) { | 358 | if (pdfContainer) { |
| 360 | - const centerX = pdfContainer.scrollLeft + pdfContainer.clientWidth / 2; | 359 | + const oldW = pdfContainer.scrollWidth; |
| 361 | - const centerY = pdfContainer.scrollTop + pdfContainer.clientHeight / 2; | 360 | + const oldH = pdfContainer.scrollHeight; |
| 361 | + const viewportW = pdfContainer.clientWidth; | ||
| 362 | + const viewportH = pdfContainer.clientHeight; | ||
| 363 | + | ||
| 364 | + const centerX = pdfContainer.scrollLeft + viewportW / 2; | ||
| 365 | + const centerY = pdfContainer.scrollTop + viewportH / 2; | ||
| 366 | + | ||
| 367 | + const anchorRatioX = oldW > 0 ? centerX / oldW : 0; | ||
| 368 | + const anchorRatioY = oldH > 0 ? centerY / oldH : 0; | ||
| 362 | 369 | ||
| 363 | - const oldZoom = zoomLevel.value; | ||
| 364 | zoomLevel.value = Math.min(3, zoomLevel.value + 0.25); | 370 | zoomLevel.value = Math.min(3, zoomLevel.value + 0.25); |
| 365 | - const newZoom = zoomLevel.value; | ||
| 366 | 371 | ||
| 367 | - // 使用nextTick确保DOM更新后再调整滚动位置 | ||
| 368 | nextTick(() => { | 372 | nextTick(() => { |
| 369 | - const zoomRatio = newZoom / oldZoom; | 373 | + const newW = pdfContainer.scrollWidth; |
| 370 | - const newScrollLeft = centerX * zoomRatio - pdfContainer.clientWidth / 2; | 374 | + const newH = pdfContainer.scrollHeight; |
| 371 | - const newScrollTop = centerY * zoomRatio - pdfContainer.clientHeight / 2; | 375 | + |
| 372 | - | 376 | + let targetCenterX = newW * anchorRatioX; |
| 373 | - pdfContainer.scrollTo({ | 377 | + let targetCenterY = newH * anchorRatioY; |
| 374 | - left: Math.max(0, newScrollLeft), | 378 | + |
| 375 | - top: Math.max(0, newScrollTop), | 379 | + let newScrollLeft = Math.max(0, targetCenterX - viewportW / 2); |
| 376 | - behavior: 'smooth' | 380 | + let newScrollTop = Math.max(0, targetCenterY - viewportH / 2); |
| 377 | - }); | 381 | + |
| 382 | + newScrollLeft = Math.min(newScrollLeft, Math.max(0, newW - viewportW)); | ||
| 383 | + newScrollTop = Math.min(newScrollTop, Math.max(0, newH - viewportH)); | ||
| 384 | + | ||
| 385 | + // 直接赋值避免平滑滚动导致的偏移抖动 | ||
| 386 | + pdfContainer.scrollLeft = newScrollLeft; | ||
| 387 | + pdfContainer.scrollTop = newScrollTop; | ||
| 378 | }); | 388 | }); |
| 389 | + } else { | ||
| 390 | + zoomLevel.value = Math.min(3, zoomLevel.value + 0.25); | ||
| 379 | } | 391 | } |
| 380 | } | 392 | } |
| 381 | }; | 393 | }; |
| ... | @@ -385,28 +397,39 @@ const zoomIn = () => { | ... | @@ -385,28 +397,39 @@ const zoomIn = () => { |
| 385 | */ | 397 | */ |
| 386 | const zoomOut = () => { | 398 | const zoomOut = () => { |
| 387 | if (zoomLevel.value > 0.5) { // 最小缩小到50% | 399 | if (zoomLevel.value > 0.5) { // 最小缩小到50% |
| 388 | - // 记录缩放前的滚动位置 | ||
| 389 | const pdfContainer = document.querySelector('.pdf-viewer-container .pdf-content'); | 400 | const pdfContainer = document.querySelector('.pdf-viewer-container .pdf-content'); |
| 390 | if (pdfContainer) { | 401 | if (pdfContainer) { |
| 391 | - const centerX = pdfContainer.scrollLeft + pdfContainer.clientWidth / 2; | 402 | + const oldW = pdfContainer.scrollWidth; |
| 392 | - const centerY = pdfContainer.scrollTop + pdfContainer.clientHeight / 2; | 403 | + const oldH = pdfContainer.scrollHeight; |
| 404 | + const viewportW = pdfContainer.clientWidth; | ||
| 405 | + const viewportH = pdfContainer.clientHeight; | ||
| 406 | + | ||
| 407 | + const centerX = pdfContainer.scrollLeft + viewportW / 2; | ||
| 408 | + const centerY = pdfContainer.scrollTop + viewportH / 2; | ||
| 409 | + | ||
| 410 | + const anchorRatioX = oldW > 0 ? centerX / oldW : 0; | ||
| 411 | + const anchorRatioY = oldH > 0 ? centerY / oldH : 0; | ||
| 393 | 412 | ||
| 394 | - const oldZoom = zoomLevel.value; | ||
| 395 | zoomLevel.value = Math.max(0.5, zoomLevel.value - 0.25); | 413 | zoomLevel.value = Math.max(0.5, zoomLevel.value - 0.25); |
| 396 | - const newZoom = zoomLevel.value; | ||
| 397 | 414 | ||
| 398 | - // 使用nextTick确保DOM更新后再调整滚动位置 | ||
| 399 | nextTick(() => { | 415 | nextTick(() => { |
| 400 | - const zoomRatio = newZoom / oldZoom; | 416 | + const newW = pdfContainer.scrollWidth; |
| 401 | - const newScrollLeft = centerX * zoomRatio - pdfContainer.clientWidth / 2; | 417 | + const newH = pdfContainer.scrollHeight; |
| 402 | - const newScrollTop = centerY * zoomRatio - pdfContainer.clientHeight / 2; | 418 | + |
| 403 | - | 419 | + let targetCenterX = newW * anchorRatioX; |
| 404 | - pdfContainer.scrollTo({ | 420 | + let targetCenterY = newH * anchorRatioY; |
| 405 | - left: Math.max(0, newScrollLeft), | 421 | + |
| 406 | - top: Math.max(0, newScrollTop), | 422 | + let newScrollLeft = Math.max(0, targetCenterX - viewportW / 2); |
| 407 | - behavior: 'smooth' | 423 | + let newScrollTop = Math.max(0, targetCenterY - viewportH / 2); |
| 408 | - }); | 424 | + |
| 425 | + newScrollLeft = Math.min(newScrollLeft, Math.max(0, newW - viewportW)); | ||
| 426 | + newScrollTop = Math.min(newScrollTop, Math.max(0, newH - viewportH)); | ||
| 427 | + | ||
| 428 | + pdfContainer.scrollLeft = newScrollLeft; | ||
| 429 | + pdfContainer.scrollTop = newScrollTop; | ||
| 409 | }); | 430 | }); |
| 431 | + } else { | ||
| 432 | + zoomLevel.value = Math.max(0.5, zoomLevel.value - 0.25); | ||
| 410 | } | 433 | } |
| 411 | } | 434 | } |
| 412 | }; | 435 | }; |
| ... | @@ -417,15 +440,10 @@ const zoomOut = () => { | ... | @@ -417,15 +440,10 @@ const zoomOut = () => { |
| 417 | const resetZoom = () => { | 440 | const resetZoom = () => { |
| 418 | const pdfContainer = document.querySelector('.pdf-viewer-container .pdf-content'); | 441 | const pdfContainer = document.querySelector('.pdf-viewer-container .pdf-content'); |
| 419 | if (pdfContainer) { | 442 | if (pdfContainer) { |
| 420 | - // 重置到顶部中央位置 | ||
| 421 | zoomLevel.value = 1; | 443 | zoomLevel.value = 1; |
| 422 | - | ||
| 423 | nextTick(() => { | 444 | nextTick(() => { |
| 424 | - pdfContainer.scrollTo({ | 445 | + pdfContainer.scrollLeft = 0; |
| 425 | - left: 0, | 446 | + pdfContainer.scrollTop = 0; |
| 426 | - top: 0, | ||
| 427 | - behavior: 'smooth' | ||
| 428 | - }); | ||
| 429 | }); | 447 | }); |
| 430 | } else { | 448 | } else { |
| 431 | zoomLevel.value = 1; | 449 | zoomLevel.value = 1; | ... | ... |
-
Please register or login to post a comment