pdf 文档搜索

This commit is contained in:
cwchen 2025-11-10 15:04:55 +08:00
parent 2fbe6901f5
commit 7b400cd9d7
1 changed files with 52 additions and 9 deletions

View File

@ -61,7 +61,7 @@ if (resolvedWorkerSrc) {
pdfjsLib.GlobalWorkerOptions.workerSrc = null pdfjsLib.GlobalWorkerOptions.workerSrc = null
} }
const DEFAULT_SCALE = 0.95 const DEFAULT_SCALE = 1
export default { export default {
name: 'DocumentSearch', name: 'DocumentSearch',
@ -107,6 +107,7 @@ export default {
standardFontDataUrl: '', standardFontDataUrl: '',
pdfAssetsBase: '', pdfAssetsBase: '',
fontsReady: new Set(), fontsReady: new Set(),
renderingSet: new Set(),
} }
}, },
mounted() { mounted() {
@ -267,6 +268,8 @@ export default {
const viewport = page.getViewport({ scale: this.scale }) const viewport = page.getViewport({ scale: this.scale })
this.ensureContainerDimensions(pageNumber, viewport) this.ensureContainerDimensions(pageNumber, viewport)
container.classList.add('is-loading')
this.renderingSet.add(pageNumber)
const oldCanvas = container.querySelector('.pdf-canvas') const oldCanvas = container.querySelector('.pdf-canvas')
if (oldCanvas) { if (oldCanvas) {
@ -276,7 +279,7 @@ export default {
const canvas = document.createElement('canvas') const canvas = document.createElement('canvas')
canvas.className = 'pdf-canvas' canvas.className = 'pdf-canvas'
const deviceScale = window.devicePixelRatio || 1 const deviceScale = window.devicePixelRatio || 1
const outputScale = Math.min(deviceScale, 1.25) const outputScale = deviceScale > 1 ? deviceScale : 1
canvas.width = viewport.width * outputScale canvas.width = viewport.width * outputScale
canvas.height = viewport.height * outputScale canvas.height = viewport.height * outputScale
canvas.style.width = `${viewport.width}px` canvas.style.width = `${viewport.width}px`
@ -284,6 +287,9 @@ export default {
container.appendChild(canvas) container.appendChild(canvas)
const canvasContext = canvas.getContext('2d') const canvasContext = canvas.getContext('2d')
if (canvasContext && 'imageSmoothingEnabled' in canvasContext) {
canvasContext.imageSmoothingEnabled = false
}
const renderContext = { const renderContext = {
canvasContext, canvasContext,
viewport, viewport,
@ -302,6 +308,8 @@ export default {
} }
this.renderedPages.set(pageNumber, { container, viewport }) this.renderedPages.set(pageNumber, { container, viewport })
container.classList.remove('is-loading')
this.renderingSet.delete(pageNumber)
return { page, viewport, container } return { page, viewport, container }
}, },
@ -318,7 +326,7 @@ export default {
const existing = container.querySelector('.textLayer') const existing = container.querySelector('.textLayer')
if (existing && !force && this.pageTextDivs[index]?.length) { if (existing && !force && this.pageTextDivs[index]?.length) {
existing.style.display = visible ? '' : 'none' existing.style.display = ''
return return
} }
if (existing) { if (existing) {
@ -329,7 +337,7 @@ export default {
textLayerDiv.className = 'textLayer' textLayerDiv.className = 'textLayer'
textLayerDiv.style.width = `${viewport.width}px` textLayerDiv.style.width = `${viewport.width}px`
textLayerDiv.style.height = `${viewport.height}px` textLayerDiv.style.height = `${viewport.height}px`
textLayerDiv.style.display = visible ? '' : 'none' textLayerDiv.style.display = ''
container.appendChild(textLayerDiv) container.appendChild(textLayerDiv)
const textLayer = new TextLayerBuilder({ const textLayer = new TextLayerBuilder({
textLayerDiv, textLayerDiv,
@ -485,6 +493,8 @@ export default {
textLayer.style.display = 'none' textLayer.style.display = 'none'
} }
container.classList.remove('is-loading')
this.renderingSet.delete(pageNumber)
container.dataset.status = this.pageTextDivs[index]?.length ? 'text-ready' : 'prefetched' container.dataset.status = this.pageTextDivs[index]?.length ? 'text-ready' : 'prefetched'
container.classList.add('prefetched') container.classList.add('prefetched')
this.renderedPages.delete(pageNumber) this.renderedPages.delete(pageNumber)
@ -647,11 +657,11 @@ export default {
this.observer = new IntersectionObserver((entries) => { this.observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => { entries.forEach((entry) => {
if (entry.isIntersecting) { if (entry.isIntersecting) {
const page = Number(entry.target.dataset.page) const page = Number(entry.target.dataset.page)
if (page) { if (page) {
this.renderTextLayer(page, { visible: true, force: true }) this.renderTextLayer(page, { visible: true, force: true })
this.scheduleRender(page, { priority: true }) this.scheduleRender(page, { priority: true })
} }
} else { } else {
const page = Number(entry.target.dataset.page) const page = Number(entry.target.dataset.page)
if (page) { if (page) {
@ -841,6 +851,39 @@ export default {
-webkit-user-select: text; -webkit-user-select: text;
} }
.pdf-page.is-loading::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 48px;
height: 48px;
margin: -24px 0 0 -24px;
border-radius: 50%;
border: 4px solid rgba(86, 119, 196, 0.25);
border-top-color: rgba(86, 119, 196, 0.9);
animation: pdf-page-spin 0.9s linear infinite;
z-index: 5;
}
.pdf-page.is-loading::after {
opacity: 0.5;
}
.pdf-page.is-loading .textLayer,
.pdf-page.is-loading .pdf-canvas {
opacity: 0.35;
}
@keyframes pdf-page-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.pdf-page > canvas.pdf-canvas { .pdf-page > canvas.pdf-canvas {
position: relative; position: relative;
top: 0; top: 0;