放大
缩小
: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放大系数
          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() {
          //获得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 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; // 不要使用 ref 或 reactive 响应式数据定义,因为下面要使用该对象中的私有方法(getPage).
       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'; // 注意版本号,一定要和 package.json 中 pdfjs-dist 的版本要一致
         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>
    复制代码
    分类:
    前端
    标签: