背景
产品需求涉及到动态的PDF展示,PDF是由后端去生成的,然后下发给前端在线的cdn地址,H5需要实现在线PDF预览的能力
方案
H5展示合同PDF,有很多实现方式。但是通过尝试后发现在不同操作系统会存在兼容性问题
最终选择了使用pdf.js,日常开发中如果原生标签能满足的还是尽量用简单的方案处理,但是此次需求涉及到较为复杂的PDF操作,所以对比之下还是使用了插件的形式
解决方案
<canvasv-for="page in pdfPages":key="page":id="'pdf-canvas' + page"/>
<script>//以es5形式引入,解决低端浏览器兼容性问题const PDFJS = require("pdfjs-dist/es5/build/pdf");//pdfjs-dist的版本也是2.5.207PDFJS.GlobalWorkerOptions.workerSrc = "/ajax/libs/pdf.js/2.5.207/pdf.worker.js";export default {name: "Contract",data: function () {return {pdfPages: [], //页数pdfWidth: "", // 宽度pdfSrc: '', //地址pdfDoc: "", //文档内容pdfScale: 1, //放大倍数contractUrl: '', //后端返回的PDF链接}},mounted() {this.getData()},methods: {getData() {getContractUrl().then(res => {if (res.data) {let {contract_url, email} = res.datathis.contractUrl = contract_urlthis.pdfSrc = contract_urlif(contract_url){this.loadFile(this.pdfSrc)}}}).catch(err => {console.log('getContractUrl err', err)})},loadFile(url) {this.$Loading.show()let loadingTask = PDFJS.getDocument(url);loadingTask.promise.then(pdf => {this.$Loading.hide()this.pdfDoc = pdf;this.pdfPages = pdf.numPages;this.$nextTick(() => {this.renderPage(1);});}).catch(err=>{console.log(err, 'loadingTask err')});},renderPage(num) {const that = this;this.pdfDoc.getPage(num).then(page => {let canvas = document.getElementById("pdf-canvas" + num);let ctx = canvas.getContext("2d");let dpr = window.devicePixelRatio || 1;let ratio = dpr;// screen.availWidth 屏幕可用宽度let viewport = page.getViewport({scale: screen.availWidth / page.getViewport({scale: this.pdfScale}).width})// console.log(viewport,'-----viewport')canvas.width = viewport.width * ratio;canvas.height = viewport.height * ratio;canvas.style.width = viewport.width + "px";canvas.style.height = viewport.height + 'px'that.pdfWidth = viewport.width + "px";canvas.style.height = viewport.height + "px";ctx.setTransform(ratio, 0, 0, ratio, 0, 0);// 将 PDF 页面渲染到 canvas 上下文中let renderContext = {canvasContext: ctx,viewport: viewport};page.render(renderContext);if (this.pdfPages > num) {this.renderPage(num + 1);}});}}</script>
踩坑点
1、移动端适配问题
需要设置好viewport,要不然canvas渲染无法做到屏幕适配
let dpr = window.devicePixelRatio || 1; //获取设备像素比let ratio = dpr// screen.availWidth 屏幕可用宽度let viewport = page.getViewport({scale: screen.availWidth / page.getViewport({scale: this.pdfScale}).width})
2、低端浏览器渲染报错environment lacks native support…
需要修改下引用pdfjs-dist的方式
之前是
const PDFJS = require("pdfjs-dist");
需要改成
const PDFJS = require("pdfjs-dist/es5/build/pdf");
参考
vue项目,npm install方式使用pdfjs
Vue 集成 PDF.js 实现 PDF 预览和添加水印
Reading pdf from url with node.js using PDF.js