参考:https://blog.csdn.net/qq_30604453/article/details/122236006
https://www.cnblogs.com/jiayouba/p/14969611.html
效果图:
<template>
<div class="pdf-touch-box">
<div class="scale-btn-box" :style="{ width: '100vw' }">
<header-bar
title="预览"
:pdfUrl="src"
:pdfDialogUrl="pdfDialogUrl"
v-on="$listeners"
></header-bar>
</div>
<div
v-show="!loading"
class="pdf-canvas-wrap"
:style="{ width: viewWidth + 'px', height: viewHeight + 'px' }"
></div>
<p class="pdf-canvas-tips" v-show="loading">正在加载...</p>
</div>
</template>
<script>
import HeaderBar from '../components/headerBar'
const CSS_UNITS = 150 / 72.0
let userAgent = (typeof navigator !== 'undefined' && navigator.userAgent) || ''
let platform = (typeof navigator !== 'undefined' && navigator.platform) || ''
let maxTouchPoints = (typeof navigator !== 'undefined' && navigator.maxTouchPoints) || 1
let maxCanvasPixels = 16777216
// PDF之外占据的宽度 -18 padding -18减去滚动条宽度(不确定)
let autoWidth = 36
let isAndroid = /Android/.test(userAgent)
let isIOS =
/\b(iPad|iPhone|iPod)(?=;)/.test(userAgent) || (platform === 'MacIntel' && maxTouchPoints > 1)
;(function checkCanvasSizeLimitation() {
if (isIOS || isAndroid) {
maxCanvasPixels = 5242880
autoWidth -= 18
}
})()
export default {
name: 'negotiateCase-mobileTerminal-pdf',
components: {
HeaderBar,
},
props: {
pdfDialogUrl: {
type: String,
default: '',
},
},
data() {
return {
src: '',
loading: true,
pdfDoc: null,
boxEl: null,
wrapEl: null,
areaWidth: 0,
viewWidth: 0,
viewHeight: 0,
pixelRatio: 2,
isFirstTimeRender: true,
viewport: null,
canvasEles: [],
canvasCtxs: [],
totallPage: 1,
pageScale: 1, // pdf 适应窗口产生的 scale
curCanvasCSSWh: null,
transform: null,
}
},
mounted() {
this.src = this.$route.params.pdfUrl || this.pdfDialogUrl
this.init()
},
methods: {
async init() {
this.boxEl = document.querySelector('.pdf-touch-box')
this.wrapEl = document.getElementsByClassName('pdf-canvas-wrap')[0]
this.areaWidth = this.boxEl.clientWidth
const loadingState = await this.getPDF()
if (loadingState === 'success') this.initRenderOneByOne()
else {
that.loading = false
this.boxEl.innerText = loadingState
}
},
getPDF() {
let that = this
return new Promise(reslove => {
window.pdfjsLib
.getDocument({
url: that.src,
cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.2.228/cmaps/',
cMapPacked: true,
})
.promise.then(
function (pdfDoc_) {
that.pdfDoc = pdfDoc_
that.totallPage = pdfDoc_.numPages
that.loading = false
reslove('success')
},
function (reason) {
console.log(reason.message)
that.loading = false
reslove(reason.name)
}
)
})
},
initRenderOneByOne() {
for (let pageNum = 1; pageNum <= this.totallPage; pageNum++) {
let canvas = document.createElement('canvas')
canvas.setAttribute('id', `pdf-canvas${pageNum}`)
canvas.setAttribute('class', `pdfcanvas`)
// alpha 设定 canvas 背景总是不透明,可以加快浏览器绘制透明的内容和图片 初始化出来 canvas 为黑色背景
// 实际上 导致 重新渲染的时候 闪黑屏
// let ctx = canvas.getContext("2d", {
// alpha: false,
// });
let ctx = canvas.getContext('2d')
this.canvasCtxs.push(ctx)
this.canvasEles.push(canvas)
this.wrapEl.appendChild(canvas)
}
this.renderSinglePage(this.canvasEles[0], 1)
},
renderSinglePage(canvas, pageNum) {
let ctx = this.canvasCtxs[pageNum - 1]
let that = this
this.pdfDoc.getPage(pageNum).then(function (page) {
if (that.isFirstTimeRender) that.initView(page, ctx)
if (pageNum === 1) that.getCanvasCSSWH()
canvas.width = that.curCanvasCSSWh.width
canvas.height = that.curCanvasCSSWh.height
canvas.style.width = that.curCanvasCSSWh.styleWidth + 'px'
canvas.style.height = that.curCanvasCSSWh.styleHeight + 'px'
canvas.style['border'] = '#d6d6d6 solid 1px'
canvas.style.margin = '9px 0 0 0'
let renderContext = {
canvasContext: ctx,
transform: that.transform,
viewport: that.viewport,
enableWebGL: false,
renderInteractiveForms: false,
}
let renderTask = page.render(renderContext)
renderTask.promise.then(function () {
if (that.totallPage >= ++pageNum) {
that.renderSinglePage(that.canvasEles[pageNum - 1], pageNum)
}
})
})
},
getCanvasCSSWH() {
let outputScale = {
sx: this.pixelRatio,
sy: this.pixelRatio,
scaled: this.pixelRatio !== 1,
}
let pixelsInViewport = this.viewport.width * this.viewport.height
let maxScale = Math.sqrt(maxCanvasPixels / pixelsInViewport)
if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
outputScale.sx = maxScale
outputScale.sy = maxScale
outputScale.scaled = true
}
let sfx = (0, this.approximateFraction)(outputScale.sx)
let sfy = (0, this.approximateFraction)(outputScale.sy)
const width = (0, this.roundToDivide)(this.viewport.width * outputScale.sx, sfx[0])
const height = (0, this.roundToDivide)(this.viewport.height * outputScale.sy, sfy[0])
const styleWidth = (0, this.roundToDivide)(this.viewport.width, sfx[1])
const styleHeight = (0, this.roundToDivide)(this.viewport.height, sfy[1])
if (this.pixelRatio !== 1) this.transform = [this.pixelRatio, 0, 0, this.pixelRatio, 0, 0]
this.viewWidth = styleWidth + 2
// 12 加上 canvas border margin 误差?2 + 9 + 1
this.viewHeight = this.totallPage * (this.viewport.height + 12) + 9
this.curCanvasCSSWh = { width, height, styleWidth, styleHeight }
return this.curCanvasCSSWh
},
approximateFraction(x) {
if (Math.floor(x) === x) {
return [x, 1]
}
var xinv = 1 / x
var limit = 8
if (xinv > limit) {
return [1, limit]
} else if (Math.floor(xinv) === xinv) {
return [1, xinv]
}
var x_ = x > 1 ? xinv : x
var a = 0,
b = 1,
c = 1,
d = 1
while (q < limit) {
var p = a + c,
q = b + d
if (q > limit) {
break
}
if (x_ <= p / q) {
c = p
d = q
} else {
a = p
b = q
}
}
var result
if (x_ - a / b < c / d - x_) {
result = x_ === x ? [a, b] : [b, a]
} else {
result = x_ === x ? [c, d] : [d, c]
}
return result
},
roundToDivide(x, div) {
var r = x % div
return r === 0 ? x : Math.round(x - r + div)
},
initView(page, ctx) {
let devicePixelRatio = window.devicePixelRatio || 1
let backingStoreRatio =
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||
1
this.pixelRatio = devicePixelRatio / backingStoreRatio
this.viewport = page.getViewport({
scale: CSS_UNITS,
})
this.pageScale = (this.areaWidth - autoWidth) / this.viewport.width
let curViewport = page.getViewport({
scale: this.pageScale * CSS_UNITS,
})
this.viewport = curViewport
this.isFirstTimeRender = false
},
},
}
</script>
<style scoped>
.pdf-touch-box {
padding: 9px;
width: calc(100% - 18px);
height: calc(100% - 18px);
display: flex;
flex-direction: column;
justify-content: center;
}
.scale-btn-box {
position: fixed;
top: 0;
left: 0;
height: 44px;
}
.pdf-canvas-wrap {
display: flex;
flex-direction: column;
align-items: center;
overflow-y: auto;
overflow-x: hidden;
margin-top: 44px;
padding-top: 9px;
}
.pdf-canvas-tips {
margin-top: 44px;
}
</style>