万丈高楼平地起 赚钱也应如是

很多时候,我们太注重技巧性的东西,而忽略了内心的建设。忽略了知识结构的建设,很容易走偏。


其实,是你方法不当,误以为学习别人成功的技巧,就等同于能够让自己快速成功。


建议带着你的现实问题,以输出内容和行动为目的,去研究、分析这个积累的过程。观察一个人、项目、产品、团队、公司……看看他们是怎样一步步成功(或失败)的,这样效果最好。

<think>我们被要求使用Vue 3的setup语法糖(<script setup>)结合pdf.js来加载PDF文件。pdf.js是Mozilla开发的一个JavaScript库,用于在网页中渲染PDF文档。 根据引用[1]和[2],我们知道有一个Vue组件`VueOfficePdf`,它来自`@vue-office/pdf`,并且它利用了pdf.js。但是用户要求使用pdf.js直接,而不是通过一个封装好的组件。不过,引用[4]提到了`vue-pdf-embed`,但用户明确要求使用pdf.js。 因此,我们需要自己使用pdf.js来实现。pdf.js的基本使用步骤包括: 1. 引入pdf.js库及其样式(如果需要默认的工具栏)。 2. 获取PDF文件(可以是URL、ArrayBuffer等)。 3. 使用`pdfjsLib.getDocument`加载PDF文档。 4. 逐页渲染到canvas上。 在Vue3的setup语法糖中,我们将使用组合式API。 由于pdf.js是一个外部库,我们需要先安装它: ```bash npm install pdfjs-dist ``` 然后,我们将创建一个Vue组件,在组件的setup中使用pdf.js。 完整示例: ```vue <template> <div> <!-- 用于显示PDF页面的容器 --> <div v-for="page in pages" :key="page.pageNumber"> <canvas :id="'canvas-' + page.pageNumber"></canvas> </div> </div> </template> <script setup> import { ref, onMounted } from 'vue'; import * as pdfjsLib from 'pdfjs-dist'; // 设置pdf.worker.js的路径,因为pdf.js使用了Web Worker来处理解析 pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.4.120/build/pdf.worker.min.js'; const props = defineProps({ pdfUrl: { type: String, required: true } }); const pages = ref([]); // 用于保存每一页的信息 // 加载PDF并渲染 const loadPdf = async () => { try { // 加载PDF文档 const pdfDoc = await pdfjsLib.getDocument(props.pdfUrl).promise; // 清空之前的页面 pages.value = []; // 循环每一页 for (let pageNum = 1; pageNum <= pdfDoc.numPages; pageNum++) { const page = await pdfDoc.getPage(pageNum); // 获取页面缩放到适合canvas的视图 const viewport = page.getViewport({ scale: 1.5 }); // 准备一个canvas元素 const canvas = document.createElement('canvas'); const canvasContext = canvas.getContext('2d'); canvas.height = viewport.height; canvas.width = viewport.width; // 将PDF页面渲染到canvas上下文中 const renderContext = { canvasContext, viewport }; await page.render(renderContext).promise; // 将canvas数据和页面信息保存 pages.value.push({ pageNumber: pageNum, canvasData: canvas.toDataURL() // 或者我们可以直接使用canvas元素,但这里为了简单使用数据URL // 或者我们也可以不在循环中渲染到canvas,而是等挂载后再渲染,所以这里我们保存页面对象和视图大小 // 下面我们采用另一种方法:在mounted之后操作DOM }); // 注意:上面的渲染方式在循环中创建了canvas,但我们没有挂载到DOM,所以需要调整。 // 实际上,我们可以在onMounted之后,使用ref来操作DOM。 } } catch (error) { console.error('Error loading PDF:', error); } }; // 在组件挂载后加载PDF onMounted(() => { loadPdf(); }); </script> ``` 但是,上面的代码在循环中创建了canvas,并且试图将每一页渲染出来,但是并没有将canvas插入到DOM中。而且,我们在模板中使用了`v-for`来遍历`pages`数组,但是数组中的每一项我们只是保存了数据URL,并没有使用canvas元素。 因此,我们需要调整策略:在模板中我们预先并不知道有多少页,所以我们一开始不生成canvas,而是等PDF加载完成后,再动态创建canvas并插入到DOM中。但是这样不太符合Vue的响式理念。 另一种做法是,我们只保存每一页的页面对象和视图大小,然后在组件的更新周期之后(使用nextTick)再进行渲染。 改进版本: 1. 我们使用一个数组`pages`来保存每一页的信息(包括页面对象和视图大小)。 2. 在模板中,我们根据`pages`数组生成对数量的canvas,并给每个canvas设置一个ref(数组)以便访问。 3. 使用`watch`或`onUpdated`来渲染每一页,但更合适的是在获取到页面信息后,利用nextTick来确保DOM已经更新,然后进行渲染。 但是,为了简单和清晰,我们可以这样做: ```vue <template> <div> <canvas v-for="page in pages" :key="page.pageNumber" :ref="setCanvasRef"></canvas> </div> </template> <script setup> import { ref, onMounted, nextTick } from 'vue'; import * as pdfjsLib from 'pdfjs-dist'; pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.4.120/build/pdf.worker.min.js'; const props = defineProps({ pdfUrl: { type: String, required: true } }); const pages = ref([]); // 保存页面信息,包括page对象和viewport const canvasRefs = ref([]); // 用于保存canvas元素的引用 const setCanvasRef = (el) => { if (el) { canvasRefs.value.push(el); } }; const loadPdf = async () => { try { const pdfDoc = await pdfjsLib.getDocument(props.pdfUrl).promise; const numPages = pdfDoc.numPages; pages.value = []; // 重置页面信息 canvasRefs.value = []; // 重置refs // 获取每一页的信息 for (let i = 1; i <= numPages; i++) { const page = await pdfDoc.getPage(i); const viewport = page.getViewport({ scale: 1.5 }); pages.value.push({ pageNumber: i, page, viewport }); } // 等待DOM更新(因为v-for会生成canvas) nextTick(() => { // 现在canvasRefs该已经更新,并且长度与pages相同 // 注意:由于我们使用同一个ref函数,所以顺序该与pages数组一致 pages.value.forEach((pageInfo, index) => { const canvas = canvasRefs.value[index]; if (!canvas) return; const context = canvas.getContext('2d'); canvas.height = pageInfo.viewport.height; canvas.width = pageInfo.viewport.width; // 渲染页面到canvas const renderContext = { canvasContext: context, viewport: pageInfo.viewport }; pageInfo.page.render(renderContext); }); }); } catch (error) { console.error('Error loading PDF:', error); } }; onMounted(loadPdf); </script> ``` 注意:上面的代码中,我们使用了一个函数`setCanvasRef`来收集canvas元素,因为v-for中的ref会返回一个数组,但是我们在setup中无法直接绑定多个ref(除非用数组ref),这里我们采用手动收集的方式。 但是,这种方法在每次更新时都会收集,可能会重复,所以我们在每次加载PDF前重置了`canvasRefs`数组。另外,由于每次加载PDF时,`pages`数组会改变,模板中的canvas会重新生成,然后触发`setCanvasRef`。 然而,上面的代码可能存在性能问题,因为每一页都渲染一个canvas,并且PDF页数可能很多。 另外,我们也可以使用单个容器,然后在加载完成后动态创建canvas并插入,但这样就需要直接操作DOM,不太符合Vue的响式原则。 考虑到用户要求完整示例,我们提供第二种方法:在加载每一页时,等待DOM更新,然后渲染到对的canvas。这种方法比较直观,但可能需要在性能上做优化(比如虚拟滚动)。 另外,pdf.js还支持使用SVG渲染,但这里我们使用canvas。 最后,我们还需要注意,在组件卸载时,最好取消PDF的加载和渲染,避免内存泄漏。 由于pdf.js的渲染是异步的,我们可以提供一个取消的方法?但是这里没有实现。 以上是一个基本的示例,用户可以根据实际需求进行调整。 另外,如果用户需要更高级的功能(如缩放、翻页等),建议使用现有的封装库,如`vue-pdf-embed`(如引用[4]所述),但用户要求直接使用pdf.js,所以我们提供了底层实现。 现在,根据要求,我们还需要生成相关问题。 §§相关问题§§ 1. 如何在Vue 3中使用pdf.js实现PDF的缩放功能? 2. 在pdf.js中,如何实现多页PDF的连续滚动? 3. 如何优化pdf.js在Vue 3中的性能(例如大型PDF)? 4. 在Vue 3中,如何使用pdf.js实现PDF文本选择和高亮? </think>以下是一个在 Vue 3 中使用 Composition API (setup 语法) 通过 PDF.js 加载和渲染 PDF 的完整示例: ```vue <template> <div class="pdf-viewer"> <!-- PDF显示容器 --> <div ref="pdfContainer" class="pdf-container"></div> <!-- 控制按钮 --> <div class="controls"> <button @click="prevPage">上一页</button> <span>第 {{ currentPage }} 页 / 共 {{ totalPages }} 页</span> <button @click="nextPage">下一页</button> <input type="range" min="1" :max="totalPages" v-model="currentPage"> </div> </div> </template> <script setup> import { ref, onMounted, onUnmounted, watch } from 'vue'; import * as pdfjsLib from 'pdfjs-dist'; // 设置PDF.js工作线程路径(必需) pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.4.120/build/pdf.worker.min.js'; const props = defineProps({ pdfUrl: { type: String, required: true } }); // 响式变量 const pdfContainer = ref(null); const pdfDoc = ref(null); const currentPage = ref(1); const totalPages = ref(0); const renderTask = ref(null); const scale = ref(1.5); // 初始缩放比例 // 加载PDF文档 const loadPdf = async () => { try { // 获取PDF文档 const loadingTask = pdfjsLib.getDocument(props.pdfUrl); pdfDoc.value = await loadingTask.promise; totalPages.value = pdfDoc.value.numPages; // 渲染第一页 renderPage(currentPage.value); } catch (error) { console.error('PDF加载失败:', error); } }; // 渲染指定页面 const renderPage = async (pageNum) => { if (!pdfDoc.value) return; // 取消正在进行的渲染任务 if (renderTask.value) { renderTask.value.cancel(); } // 清空容器 pdfContainer.value.innerHTML = ''; try { // 获取页面 const page = await pdfDoc.value.getPage(pageNum); // 创建Canvas元素 const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); // 设置视图 const viewport = page.getViewport({ scale: scale.value }); canvas.height = viewport.height; canvas.width = viewport.width; // 添加到容器 pdfContainer.value.appendChild(canvas); // 渲染页面 renderTask.value = page.render({ canvasContext: context, viewport: viewport }); await renderTask.value.promise; } catch (error) { if (error.name === 'RenderingCancelledException') { console.log('渲染已取消'); } else { console.error('页面渲染失败:', error); } } }; // 导航控制 const nextPage = () => { if (currentPage.value < totalPages.value) { currentPage.value++; } }; const prevPage = () => { if (currentPage.value > 1) { currentPage.value--; } }; // 监听页码变化 watch(currentPage, (newPage) => { renderPage(newPage); }); // 组件挂载时加载PDF onMounted(() => { loadPdf(); }); // 组件卸载时清理资源 onUnmounted(() => { if (renderTask.value) { renderTask.value.cancel(); } if (pdfDoc.value) { pdfDoc.value.destroy(); } }); </script> <style scoped> .pdf-container { margin: 20px auto; border: 1px solid #ddd; overflow: auto; max-width: 100%; } .controls { display: flex; justify-content: center; align-items: center; gap: 15px; margin-top: 15px; } canvas { display: block; margin: 0 auto; } </style> ``` ### 核心实现说明: 1. **安装依赖**: ```bash npm install pdfjs-dist ``` 2. **关键功能**: - 使用 PDF.js 的 `getDocument` 加载 PDF 文件 - 通过 `getPage` 获取特定页面 - 创建 Canvas 元素并渲染 PDF 页面 - 实现分页导航和缩放控制 - 使用 Vue 的响式系统管理状态(当前页/总页数) - 组件卸载时正确清理资源 3. **优化建议**: - 添加缩放控制:可增加 `zoomIn`/`zoomOut` 方法修改 scale 值 - 添加文本选择:启用 PDF.js 的文本层渲染 - 支持旋转:使用 `getViewport` 的 rotation 参数 - 添加加载状态:在加载期间显示加载指示器 ### 引用说明: - 基于 PDF.js 核心库实现轻量级渲染 [^1] - 使用 Vue 3 Composition API 实现响式逻辑 [^2] - 采用 Canvas 渲染确保高质量的 PDF 显示 [^1] - 通过 Props 接收 PDF 路径实现组件复用 [^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值