From 325719c7bc324ee89a580db61e3a76a590ca3f5b Mon Sep 17 00:00:00 2001 From: cwchen <1048842385@qq.com> Date: Tue, 11 Nov 2025 10:00:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3=E5=8A=A0=E8=BD=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/common/DocumentSearch.vue | 89 ++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 15 deletions(-) diff --git a/src/views/common/DocumentSearch.vue b/src/views/common/DocumentSearch.vue index a3a46b1..9332807 100644 --- a/src/views/common/DocumentSearch.vue +++ b/src/views/common/DocumentSearch.vue @@ -81,10 +81,6 @@ export default { } return null }, - isAtLastResult() { - if (!this.searchResults.length) return true - return this.currentResultIndex >= this.searchResults.length - 1 - }, }, data() { return { @@ -115,6 +111,7 @@ export default { standardFontDataUrl: '', pdfAssetsBase: '', searching: false, + scrollAnimationFrame: null, } }, mounted() { @@ -144,6 +141,7 @@ export default { }, beforeDestroy() { this.disconnectObserver() + this.cancelScrollAnimation() }, watch: { '$route.query.url'(newUrl, oldUrl) { @@ -300,10 +298,13 @@ export default { const renderContext = { canvasContext, viewport, + transform: [outputScale, 0, 0, outputScale, 0, 0], + intent: 'print', + enableWebGL: true, + background: 'rgba(255,255,255,0)', } - if (outputScale !== 1) { - renderContext.transform = [outputScale, 0, 0, outputScale, 0, 0] - } + canvasContext.setTransform(outputScale, 0, 0, outputScale, 0, 0) + canvasContext.imageSmoothingEnabled = false await page.render(renderContext).promise container.dataset.status = 'rendered' @@ -746,7 +747,8 @@ export default { await this.renderTextLayer(pageNumber, { visible: true, force: true }) await this.renderCanvas(pageNumber) await this.$nextTick() - this.highlightMatches({ preserveIndex: true, skipNavigate: true }) + this.applyHighlightsToPage(pageNumber - 1) + this.scheduleActiveHighlightRefresh() } this.currentResultIndex = index @@ -758,13 +760,12 @@ export default { if (!wrapper) return this.$nextTick(() => { - const targetRect = target.getBoundingClientRect() - const wrapperRect = wrapper.getBoundingClientRect() - const offset = targetRect.top - wrapperRect.top - wrapper.clientHeight / 2 - wrapper.scrollTo({ - top: wrapper.scrollTop + offset, - behavior: 'smooth' - }) + const container = target.closest('.pdf-page') || target + if (!container) return + const wrapperOffsetTop = container.offsetTop + const containerHeight = container.offsetHeight || target.offsetHeight || 0 + const desired = wrapperOffsetTop - Math.max((wrapper.clientHeight - containerHeight) / 2, 0) + this.smoothScrollTo(wrapper, desired) }) }, @@ -887,6 +888,51 @@ export default { }) }, + cancelScrollAnimation() { + if (this.scrollAnimationFrame !== null && typeof window !== 'undefined') { + cancelAnimationFrame(this.scrollAnimationFrame) + this.scrollAnimationFrame = null + } + }, + + smoothScrollTo(container, target, baseDuration = 240) { + if (!container) return + const maxScroll = container.scrollHeight - container.clientHeight + const finalTarget = Math.min(Math.max(target, 0), Math.max(maxScroll, 0)) + const start = container.scrollTop + const change = finalTarget - start + if (Math.abs(change) < 1) { + container.scrollTop = finalTarget + return + } + + const distanceFactor = Math.min(Math.abs(change) / Math.max(container.clientHeight, 1), 2.4) + const duration = Math.min(460, baseDuration + distanceFactor * 110) + + const startTime = performance.now() + const ease = (t) => 1 - Math.pow(1 - t, 4) + const velocityBoost = Math.min(Math.max(Math.abs(change) / 2400, 0), 0.25) + + this.cancelScrollAnimation() + + const step = (now) => { + const elapsed = now - startTime + const progress = Math.min(elapsed / duration, 1) + const eased = ease(progress) + const basePosition = start + change * eased + const overshoot = velocityBoost * Math.sin(eased * Math.PI) + container.scrollTop = Math.min(Math.max(basePosition + overshoot * change, 0), maxScroll) + + if (progress < 1) { + this.scrollAnimationFrame = requestAnimationFrame(step) + } else { + this.scrollAnimationFrame = null + } + } + + this.scrollAnimationFrame = requestAnimationFrame(step) + }, + goToPrevious() { if (!this.searchResults.length) return let targetIndex = this.currentResultIndex @@ -948,6 +994,7 @@ export default { this.disconnectObserver() this.cancelPrefetch() this.pageCache.clear() + this.cancelScrollAnimation() const wrapper = this.$refs.pdfWrapper if (wrapper) { wrapper.innerHTML = '' @@ -1040,6 +1087,13 @@ export default { background: #f4f7ff; padding: 16px; box-sizing: border-box; + font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif; + font-size: 14px; + line-height: 1.6; + color: #1f2430; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; } .search-toolbar { @@ -1096,6 +1150,10 @@ export default { // background: linear-gradient(180deg, #eef3ff 0%, #ffffff 100%); background: #eaeaea; position: relative; + scroll-behavior: smooth; + overscroll-behavior: contain; + scrollbar-gutter: stable both-edges; + -webkit-overflow-scrolling: touch; } .pdf-page { @@ -1106,6 +1164,7 @@ export default { overflow: hidden; background: #ffffff; display: block; + will-change: transform, opacity; } .pdf-page:first-of-type::before,