文档加载优化

This commit is contained in:
cwchen 2025-11-11 10:00:08 +08:00
parent 2417dd974d
commit 325719c7bc
1 changed files with 74 additions and 15 deletions

View File

@ -81,10 +81,6 @@ export default {
} }
return null return null
}, },
isAtLastResult() {
if (!this.searchResults.length) return true
return this.currentResultIndex >= this.searchResults.length - 1
},
}, },
data() { data() {
return { return {
@ -115,6 +111,7 @@ export default {
standardFontDataUrl: '', standardFontDataUrl: '',
pdfAssetsBase: '', pdfAssetsBase: '',
searching: false, searching: false,
scrollAnimationFrame: null,
} }
}, },
mounted() { mounted() {
@ -144,6 +141,7 @@ export default {
}, },
beforeDestroy() { beforeDestroy() {
this.disconnectObserver() this.disconnectObserver()
this.cancelScrollAnimation()
}, },
watch: { watch: {
'$route.query.url'(newUrl, oldUrl) { '$route.query.url'(newUrl, oldUrl) {
@ -300,10 +298,13 @@ export default {
const renderContext = { const renderContext = {
canvasContext, canvasContext,
viewport, viewport,
transform: [outputScale, 0, 0, outputScale, 0, 0],
intent: 'print',
enableWebGL: true,
background: 'rgba(255,255,255,0)',
} }
if (outputScale !== 1) { canvasContext.setTransform(outputScale, 0, 0, outputScale, 0, 0)
renderContext.transform = [outputScale, 0, 0, outputScale, 0, 0] canvasContext.imageSmoothingEnabled = false
}
await page.render(renderContext).promise await page.render(renderContext).promise
container.dataset.status = 'rendered' container.dataset.status = 'rendered'
@ -746,7 +747,8 @@ export default {
await this.renderTextLayer(pageNumber, { visible: true, force: true }) await this.renderTextLayer(pageNumber, { visible: true, force: true })
await this.renderCanvas(pageNumber) await this.renderCanvas(pageNumber)
await this.$nextTick() await this.$nextTick()
this.highlightMatches({ preserveIndex: true, skipNavigate: true }) this.applyHighlightsToPage(pageNumber - 1)
this.scheduleActiveHighlightRefresh()
} }
this.currentResultIndex = index this.currentResultIndex = index
@ -758,13 +760,12 @@ export default {
if (!wrapper) return if (!wrapper) return
this.$nextTick(() => { this.$nextTick(() => {
const targetRect = target.getBoundingClientRect() const container = target.closest('.pdf-page') || target
const wrapperRect = wrapper.getBoundingClientRect() if (!container) return
const offset = targetRect.top - wrapperRect.top - wrapper.clientHeight / 2 const wrapperOffsetTop = container.offsetTop
wrapper.scrollTo({ const containerHeight = container.offsetHeight || target.offsetHeight || 0
top: wrapper.scrollTop + offset, const desired = wrapperOffsetTop - Math.max((wrapper.clientHeight - containerHeight) / 2, 0)
behavior: 'smooth' 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() { goToPrevious() {
if (!this.searchResults.length) return if (!this.searchResults.length) return
let targetIndex = this.currentResultIndex let targetIndex = this.currentResultIndex
@ -948,6 +994,7 @@ export default {
this.disconnectObserver() this.disconnectObserver()
this.cancelPrefetch() this.cancelPrefetch()
this.pageCache.clear() this.pageCache.clear()
this.cancelScrollAnimation()
const wrapper = this.$refs.pdfWrapper const wrapper = this.$refs.pdfWrapper
if (wrapper) { if (wrapper) {
wrapper.innerHTML = '' wrapper.innerHTML = ''
@ -1040,6 +1087,13 @@ export default {
background: #f4f7ff; background: #f4f7ff;
padding: 16px; padding: 16px;
box-sizing: border-box; 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 { .search-toolbar {
@ -1096,6 +1150,10 @@ export default {
// background: linear-gradient(180deg, #eef3ff 0%, #ffffff 100%); // background: linear-gradient(180deg, #eef3ff 0%, #ffffff 100%);
background: #eaeaea; background: #eaeaea;
position: relative; position: relative;
scroll-behavior: smooth;
overscroll-behavior: contain;
scrollbar-gutter: stable both-edges;
-webkit-overflow-scrolling: touch;
} }
.pdf-page { .pdf-page {
@ -1106,6 +1164,7 @@ export default {
overflow: hidden; overflow: hidden;
background: #ffffff; background: #ffffff;
display: block; display: block;
will-change: transform, opacity;
} }
.pdf-page:first-of-type::before, .pdf-page:first-of-type::before,