放大
缩小
:style="{ width: pdf_div_width, margin: '0 auto' }"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend"
存在的问题
因为是
touchend
之后,重新渲染 pdf文件,所以手势缩放会有卡顿的感觉。解决办法引入
better-scroll
二、pdfjs 结合 better-scroll
1、package.json
"dependencies": {
"@better-scroll/core": "^2.4.2",
"@better-scroll/movable": "^2.4.2",
"@better-scroll/zoom": "^2.4.2",
"pdfjs-dist": "^2.14.305",
"vue": "^3.2.13"
<template>
<div class="home_wrap">
<div class="pdf_down">
<div class="pdf_set_left" @click="scaleD">放大</div>
<div class="pdf_set_middle" @click="scaleX">缩小</div>
</div>
<div class="pdf-parent-container" ref="scroll">
id="pdf-container"
:style="{ width: pdf_div_width, margin: '0 auto' }"
<canvas
v-for="page in pdf_pages"
:id="'the_canvas' + page"
:key="page"
></canvas>
</div>
</div>
</div>
</template>
<script>
import BScroll from '@better-scroll/core';
import Movable from '@better-scroll/movable';
import Zoom from '@better-scroll/zoom';
let PDFJS = require('pdfjs-dist');
PDFJS.GlobalWorkerOptions.workerSrc = require('pdfjs-dist/build/pdf.worker.entry.js');
BScroll.use(Movable);
BScroll.use(Zoom);
export default {
props: {
defaultSacleDelta: {
type: Number,
default: 1.1,
maxScale: {
type: Number,
default: 2,
minScale: {
type: Number,
default: 0.5,
defaultScale: {
type: Number,
default: 1,
pdfSrc: {
type: String,
default:
'http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf',
data() {
return {
currentScale: 0.5,
pdf_pages: [],
pdf_div_width: '',
startX: 0,
startY: 0,
moveX: 0,
moveY: 0,
eLen: 0,
touchDistance: null,
startTime: null,
previousPinchScale: 1,
renderMode: false,
created() {
this.currentScale = this.defaultScale;
mounted() {
this.get_pdfurl();
methods: {
scaleD() {
if (this.currentScale >= this.maxScale) {
return;
this.currentScale = this.currentScale + 0.1;
this._loadFile(this.pdfSrc);
scaleX() {
if (this.currentScale <= this.minScale) {
return;
this.currentScale = this.currentScale - 0.1;
this._loadFile(this.pdfSrc);
get_pdfurl() {
this._loadFile(this.pdfSrc);
_loadFile(url) {
console.log('_loadFile', this.currentScale);
this.renderMode = false;
let loadingTask = PDFJS.getDocument(url);
loadingTask.promise.then((pdf) => {
this.pdfDoc = pdf;
this.pdf_pages = this.pdfDoc.numPages;
this.$nextTick(() => {
this._renderPage(1);
_renderPage(num) {
const that = this;
this.pdfDoc.getPage(num).then((page) => {
let canvas = document.getElementById('the_canvas' + num);
let ctx = canvas.getContext('2d');
let dpr = window.devicePixelRatio || 1;
let bsr =
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||
let ratio = dpr / bsr;
let viewport = page.getViewport({ scale: this.currentScale });
canvas.width = viewport.width * ratio;
canvas.height = viewport.height * ratio;
canvas.style.width = viewport.width + 'px';
that.pdf_div_width = viewport.width + 'px';
canvas.style.height = viewport.height + 'px';
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
let renderContext = {
canvasContext: ctx,
viewport: viewport,
page.render(renderContext);
if (this.pdf_pages > num) {
this._renderPage(num + 1);
} else {
this.renderMode = true;
this.bs = new BScroll(this.$refs.scroll, {
bindToTarget: true,
scrollX: true,
scrollY: true,
freeScroll: true,
movable: true,
zoom: {
start: 1,
min: 0.5,
max: 5,
</script>
<style scoped>
.home_wrap {
width: 100%;
height: 100%;
.pdf_down {
position: fixed;
display: flex;
z-index: 20;
right: 26px;
bottom: 7%;
.pdf_set_left {
width: 30px;
height: 40px;
color: #408fff;
font-size: 11px;
padding-top: 25px;
text-align: center;
margin-right: 5px;
cursor: pointer;
.pdf_set_middle {
width: 30px;
height: 40px;
color: #408fff;
font-size: 11px;
padding-top: 25px;
text-align: center;
margin-right: 5px;
cursor: pointer;
.pdf-parent-container {
width: 100%;
height: 80vh;
border: 2px solid red;
overflow: scroll;
</style>
三、vite 构建的Vue项目中使用 pdfjs-dist
1、package.json
"dependencies": {
"pdfjs-dist": "^2.14.305",
"vue": "^3.2.25"
<template>
pdf-dist {{ pdfPages }}
<canvas
v-for="page in pdfPages"
:id="'the_canvas' + page"
:key="page"
></canvas>
</div>
</template>
<script>
import { nextTick, onMounted, ref } from 'vue';
export default {
setup() {
let pdfPages = ref(0);
let pdfDoc = null;
onMounted(async () => {
let PDFJS = await import('pdfjs-dist');
PDFJS.GlobalWorkerOptions.workerSrc =
'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.14.305/pdf.worker.min.js';
renderFn(PDFJS);
const renderFn = (PDFJS) => {
const url =
'http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf';
let loadingTask = PDFJS.getDocument(url);
console.log(1);
loadingTask.promise.then((pdf) => {
pdfDoc = pdf;
pdfPages.value = pdf.numPages;
nextTick(() => {
console.log('nextTick');
renderPage(1)
const renderPage = (num) => {
console.log('renderPage')
pdfDoc.getPage(num).then((page) => {
let canvas = document.getElementById('the_canvas' + num);
let ctx = canvas.getContext('2d');
let dpr = window.devicePixelRatio || 1;
let bsr =
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||
let ratio = dpr / bsr;
let viewport = page.getViewport({ scale: 1 });
canvas.width = viewport.width * ratio;
canvas.height = viewport.height * ratio;
canvas.style.width = viewport.width + 'px';
canvas.style.height = viewport.height + 'px';
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
let renderContext = {
canvasContext: ctx,
viewport: viewport,
page.render(renderContext);
if(num < pdfPages.value) {
renderPage(num + 1)
return {
pdfPages,
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
</style>
四、解决pdf放大后字体模糊 & IOS 端 canvas
绘制空白
1、字体模糊:初始化渲染的scale
大于 1,然后用 better-scroll
缩放到小尺寸
2、ISO端渲染scale
大于1时, canvas
显示空白:使用svg
绘制
<template>
<div class="home_wrap">
<div class="pdf_down">
<div class="pdf_set_left" @click="scaleD">放大</div>
<div class="pdf_set_middle" @click="scaleX">缩小</div>
<div class="pdf_set_middle" @click="onRoate">旋转</div>
<div class="pdf-parent-container" ref="scroll">
id="pdf-container"
:style="{ width: pdf_div_width, margin: '0 auto' }"
v-for="page in pdf_pages"
class="pdf-page-item"
:id="'the_page' + page"
:key="page"
</template>
<script>
import BScroll from '@better-scroll/core';
import Movable from '@better-scroll/movable';
import Zoom from '@better-scroll/zoom';
let PDFJS = require('pdfjs-dist');
PDFJS.GlobalWorkerOptions.workerSrc = require('pdfjs-dist/build/pdf.worker.entry.js');
BScroll.use(Movable);
BScroll.use(Zoom);
export default {
props: {
defaultSacleDelta: {
type: Number,
default: 1.1,
maxScale: {
type: Number,
default: 2,
minScale: {
type: Number,
default: 0.5,
defaultScale: {
type: Number,
default: 3,
pdfSrc: {
type: String,
default:
'http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf',
data() {
return {
currentScale: 5, //pdf放大系数
pdf_pages: [],
pdf_div_width: '',
startX: 0,
startY: 0,
moveX: 0,
moveY: 0,
eLen: 0,
touchDistance: null,
startTime: null,
previousPinchScale: 1,
renderMode: false,
rotateAngle: 0,
created() {
this.currentScale = 5;
mounted() {
this.get_pdfurl();
methods: {
scaleD() {
if (this.currentScale >= this.maxScale) {
return;
this.currentScale = this.currentScale + 0.1;
this._loadFile(this.pdfSrc);
scaleX() {
if (this.currentScale <= this.minScale) {
return;
this.currentScale = this.currentScale - 0.1;
this._loadFile(this.pdfSrc);
onRoate() {
this.rotateAngle += 90;
if (this.rotateAngle >= 360) {
this.rotateAngle = 0;
let pages = document.querySelectorAll('.pdf-page-item');
pages.forEach((el) => {
el.style.transform = `rotate(${this.rotateAngle}deg)`;
get_pdfurl() {
//获得pdf教案
//加载本地
this._loadFile(this.pdfSrc);
//线上请求
// this.$axios.get('')
// .then((res)=>{
// this.pdfSrc = res.url
// this._loadFile(this.pdfSrc)
// })
_loadFile(url) {
console.log('_loadFile', this.currentScale);
this.renderMode = false;
let loadingTask = PDFJS.getDocument(url);
loadingTask.promise.then((pdf) => {
this.pdfDoc = pdf;
this.pdf_pages = this.pdfDoc.numPages;
this.$nextTick(() => {
this._renderPage(1);
_renderPage(num) {
const that = this;
this.pdfDoc.getPage(num).then((page) => {
let pageContainer = document.getElementById('the_page' + num);
let viewport = page.getViewport({ scale: this.currentScale });
pageContainer.style.width = viewport.width + 'px';
that.pdf_div_width = viewport.width + 'px';
pageContainer.style.height = viewport.height + 'px';
page.getOperatorList().then((opList) => {
var svgFx = new PDFJS.SVGGraphics(page.commonObjs, page.objs);
return svgFx.getSVG(opList, viewport).then(function (svg) {
pageContainer.appendChild(svg);
if (this.pdf_pages > num) {
this._renderPage(num + 1);
} else {
this.renderMode = true;
this.$nextTick(() => {
this.bs = new BScroll(this.$refs.scroll, {
bindToTarget: true,
scrollX: true,
scrollY: true,
freeScroll: true,
movable: true,
zoom: {
start: 0.2,
min: 0.1,
max: 5,
// this.bs.zoomTo(0.1, 'center', 'center');
let flag = true;
this.bs.on('zoomEnd', ({ scale }) => {
// use scale
if (!flag) {
return;
this.bs.zoomTo(1, 100, 100);
flag = !flag;
console.log(scale); // 当前 scale 的值
this.currentScale = scale;
this._renderPage(1);
</script>
<style scoped>
.home_wrap {
width: 100%;
height: 100%;
.pdf_down {
position: fixed;
display: flex;
z-index: 20;
right: 26px;
bottom: 7%;
.pdf_set_left {
width: 30px;
height: 40px;
color: #408fff;
font-size: 11px;
padding-top: 25px;
text-align: center;
margin-right: 5px;
cursor: pointer;
.pdf_set_middle {
width: 30px;
height: 40px;
color: #408fff;
font-size: 11px;
padding-top: 25px;
text-align: center;
margin-right: 5px;
cursor: pointer;
.pdf-parent-container {
width: 100%;
height: 80vh;
border: 2px solid red;
overflow: hidden;
</style>
复制代码