我希望封装一些文档/页面加载逻辑在Vue组件,但我有麻烦让它工作。这是我尝试过的。(如果它是相关的,我使用Vite + Vue + TS与"pdfjs-dist": "^2.14.305"
,"vue": "^3.2.25"
。)
首先,我有一个名为PDFDocument的组件。它被传递PDF URL和缩放作为道具,它处理获取文档和加载页面。
// PDFDocument.vue
<template>
<div class="pdf-document">
<PDFPage v-for="page in pages" v-bind="{page, scale}" :key="page.pageNumber"/>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import * as pdfjsLib from 'pdfjs-dist';
import PDFPage from './PDFPage.vue';
pdfjsLib.GlobalWorkerOptions.workerSrc = "../../node_modules/pdfjs-dist/build/pdf.worker.js";
export default defineComponent({
props: {
url: { type: String, required: true },
scale: { type: Number, required: true },
},
data() {
let pdf: pdfjsLib.PDFDocumentProxy | undefined;
let pages: pdfjsLib.PDFPageProxy[] = [];
return { pdf, pages };
},
beforeMount() {
let loadingTask = pdfjsLib.getDocument(this.url);
loadingTask.promise.then((pdf) => {
this.pdf = pdf;
const promises = [];
for (let i = 1; i <= pdf.numPages; i++) {
promises.push(pdf.getPage(i));
}
Promise.all(promises).then(pages => {
this.pages = pages;
console.log(this.pages);
});
});
},
components: { PDFPage }
})
</script>
这似乎工作,但我已经遇到了我的第一个问题。如果我尝试使用this.pdf.getPage(i)
而不是pdf.getPage(i)
,我得到一个错误:Uncaught (in promise) TypeError: Cannot read from private field
。但是现在只使用pdf
修复了这个问题,所以好的。
然后我有一个PDFPage组件,处理设置一个页面的画布,并让PDFPageProxy
渲染到它。
// PDFPage.vue
<template>
<canvas
class="pdf-page"
:width="width"
:height="height"
:style="canvasStyle"
></canvas>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import * as pdfjsLib from 'pdfjs-dist';
pdfjsLib.GlobalWorkerOptions.workerSrc = "../../node_modules/pdfjs-dist/build/pdf.worker.js";
export default defineComponent({
props: {
page: {type: pdfjsLib.PDFPageProxy, required: true},
scale: {type: Number, required: true},
},
data() {
let viewport: pdfjsLib.PageViewport | undefined;
let renderTask: pdfjsLib.RenderTask | undefined;
return { viewport, renderTask }
},
computed: {
width() { if (!this.viewport) return 0; return this.viewport.width; },
height() { if (!this.viewport) return 0; return this.viewport.height; },
canvasStyle() {
const {width: actualWidth, height: actualHeight} = this.actualSizeViewport;
const pxRatio = window.devicePixelRatio || 1;
const [pxWidth, pxHeight] = [actualWidth, actualHeight]
.map(dim => Math.ceil(dim / pxRatio));
return `width: ${pxWidth}px; height: ${pxHeight}px;`
},
actualSizeViewport() {
return (this.viewport as pdfjsLib.PageViewport).clone({scale: this.scale});
},
},
methods: {
drawPage() {
if (this.renderTask) return;
const {viewport} = this;
const canvasContext = this.$el.getContext('2d');
const renderContext = {canvasContext, viewport};
this.renderTask = this.page.render(renderContext);
(this.renderTask as pdfjsLib.RenderTask).promise.then(
() => this.$emit('rendered', this.page)
);
}
},
created() {
this.viewport = this.page.getViewport(this.scale);
},
mounted() {
this.drawPage();
}
})
</script>
在这里,当尝试做this.renderTask = this.page.render(renderContext)
时,我得到错误Uncaught (in promise) TypeError: Cannot read from private field
(或有时Cannot write to private field
),我认为这是一个问题,因为我使用this.page
。
有人知道如何绕过这个吗?我觉得这可能与这些Proxy
对象声称是工作线程的代理这一事实有关,我可能不尊重这一点?
经过一番调查,我想我找到了罪魁祸首。我不确定这是一个PDF.js的东西或Vue 3的东西,但this.pdf
和this.page
对象是Proxy
s,而不是对象本身。从Vue导入toRaw
并使用toRaw(this.pdf).getPage(i)
或toRaw(this.page).render(renderContext)
似乎已经解决了这个问题。(参考:https://stackoverflow.com/a/70805174/18837571。)
(注:此外,PDFPage组件中的this.page.getViewport
需要为this.page.getViewport({scale = this.scale})
才能使渲染工作!)