diff --git a/demo/App.vue b/demo/App.vue index 1d242e1..274e52b 100644 --- a/demo/App.vue +++ b/demo/App.vue @@ -1,13 +1,15 @@ - switch url 1 - switch url 2 - switch base64 - + + switch url + + switch url 1 + switch url 2 + switch base64 + + + + @@ -15,7 +17,8 @@ import PDFViewer from '../src/index.js' const TEST_URL_MAP = { - test1: 'https://raw.githubusercontent.com/DingRui12138/vue-pdf-viewer/master/demo/static/pdf/helloworld.pdf', + test1: + 'https://raw.githubusercontent.com/DingRui12138/vue-pdf-viewer/master/demo/static/pdf/helloworld.pdf', test2: 'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf', base64: `data:application/octet-stream;base64,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`, @@ -27,7 +30,7 @@ export default { }, data() { return { - pdfSource: TEST_URL_MAP.test2, + pdfSource: TEST_URL_MAP.test1, } }, methods: { diff --git a/src/PDFViewer.vue b/src/PDFViewer.vue index 4b0b9ce..aba2497 100644 --- a/src/PDFViewer.vue +++ b/src/PDFViewer.vue @@ -1,6 +1,6 @@ - + - + - Loading {{dotText}} + + {{ loadingContent }} + + + + + {{ renderingContent }} + { + this._timer && clearInterval(this._timer) + this.seconds = 0 + + this._timer = setInterval(() => { + this.seconds += 1 + }, 500) + } + + if (isLoading) { + if (this.$slots.loading) return + startTimer() + } else if (isRendering) { + if (this.$slots.rendering) return + startTimer() + } + }, + }, + deep: true, + }, methods: { handleDownload() { - this.$emit('download', this.source) + this.$emit('download', { + src: this.source, + filename: this.filename, + }) }, handlePrint() { this.$refs.viewer.print() @@ -134,22 +183,16 @@ export default { this.total = total this.$emit('loaded', params) + this.isLoading = false }, handleDocumentRender() { - this.isLoading = false this.$emit('rendered') }, + handleUpdateRenderingState(isRendering) { + this.isRendering = isRendering + }, handleUpdateLoadingState(isLoading) { this.isLoading = isLoading - - if (this.$slots.loading) return - this._timer && clearInterval(this._timer) - if (isLoading) { - this.seconds = 0 - this._timer = setInterval(() => { - this.seconds += 1 - }, 500) - } }, handlePasswordRequest({ callback, retry }) { // TODO: slot dialog ? @@ -207,6 +250,17 @@ export default { padding: 0 16px; box-shadow: 0px 3px 10px 2px black; z-index: 999; + &.not-ready { + position: relative; + &::after { + content: ''; + height: 100%; + width: 100%; + display: inline-block; + position: absolute; + z-index: 1; + } + } } &__body { @@ -222,7 +276,9 @@ export default { height: 100%; width: 100%; pointer-events: none; - .loading-content { + background: #a9a9a9; + .loading-content, + .rendering-content { height: 100%; width: 100%; display: flex; diff --git a/src/components/RotateWrapper/RotateWrapper.scss b/src/components/RotateWrapper/RotateWrapper.scss new file mode 100644 index 0000000..03ec840 --- /dev/null +++ b/src/components/RotateWrapper/RotateWrapper.scss @@ -0,0 +1,15 @@ +.rotate-wrapper { + display: inline-block; + position: relative; + width: 100%; + .image { + position: absolute; + width: 100%; + } + &::after { + content: ''; + width: var(--content-width); + height: var(--content-height); + display: inline-block; + } +} diff --git a/src/components/RotateWrapper/RotateWrapper.vue b/src/components/RotateWrapper/RotateWrapper.vue new file mode 100644 index 0000000..59dc67a --- /dev/null +++ b/src/components/RotateWrapper/RotateWrapper.vue @@ -0,0 +1,79 @@ + + + + + + + + + + diff --git a/src/components/Viewer/Viewer.scss b/src/components/Viewer/Viewer.scss index 0dd905b..2e450ab 100644 --- a/src/components/Viewer/Viewer.scss +++ b/src/components/Viewer/Viewer.scss @@ -17,7 +17,7 @@ &.visible { margin-left: 0; } - .catalog-content { + .catalog-container { padding-bottom: 20px; .catalog-item { color: white; @@ -34,16 +34,22 @@ height: 14px; line-height: 14px; } - .canvas { - width: 110px; - // height: 152px; - opacity: 0.8; - &.active { - box-shadow: 0 0 0 4px rgb(84, 201, 255); - opacity: 1; + &__content { + + * { + width: 110px; + // height: 152px; + opacity: 0.8; + transform: var(--item-transform); } - &:hover { - opacity: 1; + &.active { + img { + box-shadow: 0 0 0 4px rgb(84, 201, 255); + opacity: 1; + &:hover { + opacity: 1; + } + } } } } @@ -56,20 +62,25 @@ .viewer-item { padding-bottom: 20px; font-size: 0; - display: flex; - justify-content: center; - align-items: center; + // display: flex; + // justify-content: center; + // align-items: center; width: 100%; page-break-after: always; - .canvas { - white-space: nowrap; - // margin: 0 auto; - // display: block; - margin: initial; - max-width: initial; - // margin-bottom: 20px; - box-shadow: 0px 0px 7px 6px rgba($color: #000000, $alpha: 0.25); + > div { transition: transform 300ms ease; + flex-shrink: 0; + box-shadow: 0px 0px 7px 6px rgba($color: #000000, $alpha: 0.25); + margin: 0 auto; + .placeholder { + width: 100%; + white-space: nowrap; + // margin: 0 auto; + // display: block; + margin: initial; + max-width: initial; + // margin-bottom: 20px; + } } } } @@ -96,9 +107,14 @@ .viewer-item { page-break-after: always!important; padding: 0!important; - .canvas { + > div { width: 100% !important; + transform: rotate(0)!important; box-shadow: none!important; + .placeholder { + width: 100% !important; + box-shadow: none!important; + } } } } diff --git a/src/components/Viewer/Viewer.vue b/src/components/Viewer/Viewer.vue index ccb65ee..95b9cb8 100644 --- a/src/components/Viewer/Viewer.vue +++ b/src/components/Viewer/Viewer.vue @@ -1,5 +1,5 @@ - + - + - - + + > + + {{ page }} @@ -38,12 +39,22 @@ > - + + + + + + @@ -56,13 +67,22 @@ import * as PDF from 'pdfjs-dist/es5/build/pdf.js' import PDFWorker from 'pdfjs-dist/es5/build/pdf.worker.js' import IFrame from '../IFrame/IFrame.vue' import throttle from '../../utils/throttle' -import style from '!!css-loader!!sass-loader!./Viewer.scss' +import viewerStyle from '!!css-loader!!sass-loader!./Viewer.scss' +import getPageBlobList from './getPageBlobList.js' +import RotateWrapper from '../RotateWrapper/RotateWrapper.vue' +import rotateWrapperStyle from '!!css-loader!!sass-loader!../RotateWrapper/RotateWrapper.scss' PDF.GlobalWorkerOptions.workerPort = new PDFWorker() const MARGIN_OFFSET = 20 const NORMAL_RATIO = 2 +const replacePlaceholder = (container, target) => { + const placeholder = container.querySelector('.placeholder') + + placeholder.replaceWith(target) +} + export default { name: 'Viewer', props: { @@ -77,9 +97,11 @@ export default { required: true, }, }, - style: style.toString(), + viewerStyle: viewerStyle.toString(), + rotateWrapperStyle: rotateWrapperStyle.toString(), components: { IFrame, + RotateWrapper, }, data() { return { @@ -87,6 +109,8 @@ export default { viewerContentHeight: 0, viewportHeight: 0, viewportWidth: 0, + imageList: [], + viewerImageList: [], } }, computed: { @@ -124,11 +148,6 @@ export default { } : {} }, - thumbnailStyle() { - return { - transform: `rotate(${this.rotate}deg)`, - } - }, }, watch: { isFullpage(n, o) { @@ -163,6 +182,7 @@ export default { this.render() }, mounted() { + this.$refs.iframe.appendStyle(this.$options.rotateWrapperStyle) // TODO: element resize replace window resize with Observe this.handleResize = throttle(() => { this.viewportHeight = this.$refs.container.clientHeight @@ -260,47 +280,43 @@ export default { return } try { + this.$emit('update:isRendering', true) await this.$nextTick() - await Promise.all( - this.pages.map(async (pageNum, i) => { - const page = await this.pdf.getPage(pageNum) - const pageWidth = page.view[2] - const containerWidth = this.$el.clientWidth - const targetWidth = containerWidth * 0.9 - const scale = targetWidth / pageWidth - // const scale = Math.ceil(this.$el.clientWidth / page.view[2]) + 1 + const blobs = await getPageBlobList(this.pages, this.pdf) - const viewport = page.getViewport({ - scale: scale, - }) - // render viewer - const viewerCanvas = this.$refs.viewerCanvas[i] - viewerCanvas.width = viewport.width - viewerCanvas.height = viewport.height + const getImage = blobData => { + const image = document.createElement('img') + image.className = 'placeholder' + image.src = URL.createObjectURL(blobData.blob) - const renderViewer = page.render({ - canvasContext: viewerCanvas.getContext('2d'), - viewport, - }).promise + return image + } - // render catalog - const catalogScale = 110 / pageWidth - const catalogViewport = page.getViewport({ - scale: catalogScale, - }) - const catalogCanvas = this.$refs.catalogCanvas[i] - catalogCanvas.width = catalogViewport.width - catalogCanvas.height = catalogViewport.height + this.imageList = [] + const promiseList = blobs.map((blobData, idx) => { + const image = getImage(blobData) + this.imageList = [...this.imageList, image.src] + // const catalogImg = image.cloneNode() + // replacePlaceholder(this.$refs.catalogItemContent[idx], catalogImg) + const viewerImg = image.cloneNode() + this.viewerImageList = [...this.viewerImageList, viewerImg.src] + replacePlaceholder(this.$refs.viewerItem[idx], viewerImg) - const renderCatalog = page.render({ - canvasContext: catalogCanvas.getContext('2d'), - viewport: catalogViewport, - }) - await Promise.all([renderViewer, renderCatalog]) + // const catalogLoaded = new Promise(resolve => { + // catalogImg.onload = resolve + // }) + const viewerLoaded = new Promise(resolve => { + viewerImg.onload = resolve }) - ) + + return Promise.all([/*catalogLoaded, */ viewerLoaded]) + }) + + await Promise.all(promiseList) + this.$emit('rendered') + this.$emit('update:isRendering', false) await this.$nextTick() this.viewerContentHeight = this.$refs.viewerContent.clientHeight @@ -315,7 +331,7 @@ export default { }, syncViewerOffset(page) { if (this.isScrolling) return - this.$refs.viewerCanvas[page - 1].scrollIntoView() + this.$refs.viewerItem[page - 1].scrollIntoView() }, syncCatalogOffset(page) { const target = this.$refs.catalogItem[page - 1] diff --git a/src/components/Viewer/getPageBlobList.js b/src/components/Viewer/getPageBlobList.js new file mode 100644 index 0000000..3d2f1d2 --- /dev/null +++ b/src/components/Viewer/getPageBlobList.js @@ -0,0 +1,57 @@ +const getPageBlobList = async (pages, pdf) => { + const container = getContainer() + + const promises = pages.map(pageNum => { + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + try { + const page = await pdf.getPage(pageNum) + + renderPage(page, container).then(resolve) + } catch (e) { + reject(e) + } + }) + }) + const data = await Promise.all(promises) + + container.remove() + + return data +} + +const renderPage = async (page, container) => { + // This gives us the page's dimensions at full scale + const viewport = page.getViewport({ + scale: 3, + }) + + // We'll create a canvas for each page to draw it on + const canvas = document.createElement('canvas') + canvas.style.display = 'block' + container.appendChild(canvas) + + const context = canvas.getContext('2d') + canvas.height = viewport.height + canvas.width = viewport.width + + // Draw it on the canvas + await page.render({ canvasContext: context, viewport }).promise + + return new Promise(resolve => + canvas.toBlob(result => + resolve({ blob: result, height: viewport.height, width: viewport.width }) + ) + ) +} + +const getContainer = () => { + const container = document.createElement('div') + container.style.visibility = 'hidden' + container.style.display = 'none' + document.body.appendChild(container) + + return container +} + +export default getPageBlobList