PDF.js 渲染存档 + 疑难杂症
当前使用部分如下
前端部分
<template>
<div class="container" style="margin: 0;padding: 0;">
<div id="pdf-view" class="pdf-view"> <!-- 渲染PDF -->
<div v-for="page in state.pdfPages" :key="page" class="page-container">
<canvas :id="`canvas-page-${page}`" class="pdf-canvas"></canvas>
</div>
<div id="text-view"></div>
</div>
<v-navigation-drawer permanent location="right" :width="500" class="floating-drawer" style="position: fixed;">
<v-list dense>
<v-list-item v-for="item in items" :key="item.title">
<v-list-item-icon>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-navigation-drawer>
<!-- 浮动抽屉 -->
</div>
</template>
脚本
<script setup>
//需要用到的文件
import * as pdfjsLib from 'pdfjs-dist';
import { TextLayerBuilder, EventBus } from 'pdfjs-dist/web/pdf_viewer'; // 从 pdf_viewer.js 引入
import 'pdfjs-dist/web/pdf_viewer.css';
import { reactive, onMounted, nextTick } from 'vue';
// 文件路径
import pdf from '/Documents/computer_net1.pdf';
const state = reactive({
pdfPath: pdf, // PDF 文件路径
pdfPages: 1, // PDF 总页数
pdfScale: 1.5, // 缩放比例
});
const items = [
{ title: 'Home', icon: 'mdi-home' },
{ title: 'Settings', icon: 'mdi-account' },
{ title: 'Logout', icon: 'mdi-logout' }
];
let pdfDoc = null;
onMounted(() => {
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';
loadFile(state.pdfPath);
});
function loadFile (url) {
pdfjsLib
.getDocument({
url,
cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.16.105/cmaps/',
cMapPacked: true,
})
.promise.then((pdf) => {
pdfDoc = pdf;
state.pdfPages = pdf.numPages;
nextTick(() => {
renderPages(); // 渲染所有页面
});
})
.catch((error) => {
console.error('Error loading PDF:', error);
});
}
function renderPages () {
for (let num = 1; num <= state.pdfPages; num++) {
renderPage(num);
}
}
function renderPage (num) {
pdfDoc.getPage(num).then((page) => {
const viewport = page.getViewport({ scale: state.pdfScale });
// 渲染 Canvas 层
const canvas = document.getElementById(`canvas-page-${num}`);
const ctx = canvas.getContext('2d');
canvas.width = viewport.width;
canvas.height = viewport.height;
const renderContext = {
canvasContext: ctx,
viewport,
};
// 获取文本内容和渲染页面的 Promise
const getTextContentPromise = page.getTextContent();
const renderPagePromise = page.render(renderContext);
Promise.all([getTextContentPromise, renderPagePromise])
.then(([textContent]) => {
const textLayerDiv = document.createElement('div');
textLayerDiv.setAttribute('class', 'textLayer');
// 设置容器样式
textLayerDiv.style.position = 'absolute';
textLayerDiv.style.left = canvas.offsetLeft + 'px';
textLayerDiv.style.top = canvas.offsetTop + 'px';
textLayerDiv.style.height = canvas.offsetHeight + 'px';
textLayerDiv.style.width = canvas.offsetWidth + 'px';
textLayerDiv.style.zIndex = '1';
textLayerDiv.style.opacity = '1';
const textView = document.querySelector('#text-view');
textView.appendChild(textLayerDiv);
// 正确使用 EventBus
const eventBus = new EventBus();
const textLayer = new TextLayerBuilder({
textLayerDiv,
pageIndex: page.pageIndex,
viewport,
eventBus,
});
textLayer.setTextContent(textContent);
textLayer.render();
})
.catch((error) => {
console.error('Error rendering text layer:', error);
});
});
}
</script>
CSS部分
<style>
.textLayer span::selection {
color: white;
/* 选中文本的颜色 */
background: rgba(0, 0, 255, 0.5);
/* 半透明蓝色背景 */
}
/* 抽屉样式 */
.floating-drawer {
height: 100vh;
/* 抽屉高度固定占满整个视口 */
position: sticky;
/* 抽屉固定在右侧 */
right: 0;
/* 靠右 */
top: 0;
/* 从页面顶部开始 */
overflow-y: auto;
/* 抽屉内容可以滚动 */
box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1);
/* 可选:增加阴影效果 */
background-color: rgb(192, 191, 191);
/* 背景颜色 */
z-index: 999;
}
.pdf-view {
height: 100%;
overflow: auto;
}
</style>
另外有一些比较值得注意的点如下。
- 如果需要从后端获取静态路径文件PDF,那么代码参考上一篇文章。其中,需要留意到vite.config.json
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vuetify({ autoImport: true }), // 配置 vuetify 插件
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
proxy: {
// 代理静态文件请求到 Django 服务器
'/static': 'http://127.0.0.1:8000' //问题1:static重复,问题2:后端ipv4 16位,前端ipv6,32位,回环,强制改为127.0.0.1
},
},
})
前端部分
const fullPdfUrl = `${this.pdfUrl}`; //不取完整路径
2.关于抽屉的问题
position:fixed 不能和 position:absolute并存 ,且需要父子盒(类)
我与你同行。

浙公网安备 33010602011771号