Error: Cannot use the same canvas during multiple render() operations. PDF.js issue

1 篇文章 0 订阅
1 篇文章 0 订阅

现象:多次重复渲染后,偶发出现canvas绘制错误问题

解决思路:监听页面渲染进度,完成后加载下一页

核心源码:

完整源码组件如下:

<template>
  <div class="pdf-view">
    <canvas
      class="canvas-screen-normal"
      :id="`pdfCanvas${page}`"
      v-for="page in dataMap.pdfPages"
      :key="page"
    ></canvas>
  </div>
</template>

<script setup lang="ts">
  import { reactive, nextTick, watch, ref } from 'vue';
  import * as PDF from 'pdfjs-dist';
  import entry from 'pdfjs-dist/build/pdf.worker.entry'; // 引入时会报红线错误,不影响运行, 或在index.d.ts中声明declare

  const props = defineProps({
    height: {
      type: String,
      default: '',
    },
    width: {
      type: String,
      default: '',
    },
    // PDF文件路径url
    pdfPath: {
      type: String,
      default: '',
    },
  });

  watch(
    () => props.pdfPath,
    (newVal) => {
      if (newVal) {
        console.log('watch-pdfjsUrl:', newVal);
        // 延迟加载,避免组件方法未初始化完成造成的加载错误问题
         setTimeout(() => {
          loadFile(newVal);
         }, 100);
      }
    },
    { immediate: true, deep: true },
  );

  const dataMap = reactive<any>({
    pdfPages: [], // 页数
    pdfWidth: '', // 宽度
    pdfDoc: '', // 文档内容
    pdfScale: 2.0, // 放大倍数(如果预览内容模糊的情况可继续放大倍数增加清晰度)
    pageRendering: false, // pdf页面是否正在渲染中
    pageNumPending: null, // pdf正在加载中的页码
  });

  const loadFile = (url: string) => {
    if (!PDF.GlobalWorkerOptions.workerSrc) {
      PDF.GlobalWorkerOptions.workerSrc = entry;
    }
    console.log('loadPdfFile:', url);
    
    let loadingTask = PDF.getDocument(url);
    loadingTask.promise.then((pdf: any) => {
      dataMap.pdfDoc = pdf;
      dataMap.pdfPages = pdf.numPages;
      nextTick(() => {
        pdfStreamRenderPage(1);
      });
    });
  };

  const pdfStreamRenderPage = (num) => {
    // 一串pdf64位的流
    if (dataMap.pageRendering) {
      dataMap.pageNumPending = num;
    } else {
      const pageNum = 1
      renderPage(pageNum)
    }
  };

  const renderPage = (num: number) => {
    dataMap.pageRendering = true;
    dataMap.pdfDoc.getPage(num).then((page: any) => {
      let canvas: any = document.getElementById('pdfCanvas' + num);
      let ctx = canvas.getContext('2d');
      let dpr = window.devicePixelRatio || 1;
      let bsr =
        ctx.webkitBackingStorePixelRatio ||
        ctx.mozBackingStorePixelRatio ||
        ctx.msBackingStorePixelRatio ||
        ctx.oBackingStorePixelRatio ||
        ctx.backingStorePixelRatio ||
        1;
      let ratio = dpr / bsr;
      let viewport = page.getViewport({ scale: dataMap.pdfScale });
      canvas.width = viewport.width * ratio;
      canvas.height = viewport.height * ratio;
      // canvas.style.width = viewport.width + "px";
      // canvas.style.height = viewport.height + "px";
      canvas.style.width = '100%';
      canvas.style.height = '100%';
      // dataMap.pdfWidth = viewport.width + 'px';
      // 将 PDF 页面渲染到 canvas 上下文中
      let renderContext = {
        transform: [ratio, 0, 0, ratio, 0, 0], //同比例缩放
        canvasContext: ctx,
        viewport: viewport,
      };

      // 解决如果操作频率过快或者多次执行页面展示的pdf文件内容丢失,展示不完整,内容倒置等现象。
      // 打开控制台发现有报错信息:ERROR Error: Uncaught (in promise): Error: Cannot use the same canvas during multiple render() operations. Use different canvas or ensure previous operations were cancelled or completed
      const renderTask = page.render(renderContext);
      // Wait for rendering to finish
      renderTask.promise.then(() => {
        dataMap.pageRendering = false;
        num++;
        if (num <= dataMap.pdfPages) {
          // scale next canvas
          renderPage(num)
        }
        if (dataMap.pageNumPending !== null) {
          // New page rendering is pending
          pdfStreamRenderPage(dataMap.pageNumPending);
          dataMap.pageNumPending = null;
        }
      });
    });
  };
</script>

<style lang="less" scoped>
  .pdf-view {
    width: 100%;
    margin: 0 auto;
    border: 1px solid #e4ebf1;
    position: relative;
    overflow: scroll;
    overflow-x: hidden;
    .canvas-screen-normal {
      width: 100% !important;
      height: auto !important;
      display: block;
      border-bottom: 1px solid #e4ebf1;
    }
    canvas:last-child {
      border-bottom: none;
    }
  }
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值