You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js中PDF.js偶现页面倒置及渲染报错问题排查

Vue.js 集成 PDF.js 时页面倒置+重复渲染报错的解决方案

问题现象

  • PDF页面偶尔出现显示倒置的情况
  • 控制台抛出错误:pdf.min.js:22 Uncaught (in promise) Error: Cannot use the same canvas during multiple render() operations. Use different canvas or ensure previous operations were cancelled or completed.
  • 即便用Promise处理了页面渲染逻辑,问题仍然存在

问题复现代码

<template>
  <canvas ref="pdfCanvas"></canvas>
</template>

<script>
import * as pdfjsLib from 'pdfjs-dist'
import 'pdfjs-dist/build/pdf.worker.entry'

export default {
  data() {
    return {
      pdfDoc: null,
      currentPage: 1
    }
  },
  methods: {
    async loadPdf(url) {
      this.pdfDoc = await pdfjsLib.getDocument(url).promise
      this.renderPage(this.currentPage)
    },
    async renderPage(pageNum) {
      const page = await this.pdfDoc.getPage(pageNum)
      const canvas = this.$refs.pdfCanvas
      const ctx = canvas.getContext('2d')
      const viewport = page.getViewport({ scale: 1.5 })
      
      canvas.width = viewport.width
      canvas.height = viewport.height
      
      // 直接发起渲染,未处理上一次的渲染任务
      await page.render({
        canvasContext: ctx,
        viewport
      }).promise
    }
  }
}
</script>

修复后无异常代码

<template>
  <canvas ref="pdfCanvas"></canvas>
</template>

<script>
import * as pdfjsLib from 'pdfjs-dist'
import 'pdfjs-dist/build/pdf.worker.entry'

export default {
  data() {
    return {
      pdfDoc: null,
      currentPage: 1,
      renderTask: null // 保存当前渲染任务
    }
  },
  methods: {
    async loadPdf(url) {
      this.pdfDoc = await pdfjsLib.getDocument(url).promise
      this.renderPage(this.currentPage)
    },
    async renderPage(pageNum) {
      // 先取消上一次未完成的渲染任务
      if (this.renderTask) {
        this.renderTask.cancel()
      }
      
      const page = await this.pdfDoc.getPage(pageNum)
      const canvas = this.$refs.pdfCanvas
      const ctx = canvas.getContext('2d')
      
      // 读取页面旋转角度,修正视口
      const rotate = page.rotate
      const viewport = page.getViewport({ scale: 1.5, rotate })
      
      canvas.width = viewport.width
      canvas.height = viewport.height
      
      // 清空画布避免残留内容
      ctx.clearRect(0, 0, canvas.width, canvas.height)
      
      // 保存当前渲染任务,便于后续取消
      this.renderTask = page.render({
        canvasContext: ctx,
        viewport
      })
      
      try {
        await this.renderTask.promise
      } catch (err) {
        // 仅捕获非取消类的渲染异常
        if (!err.message.includes('cancelled')) {
          console.error('PDF渲染失败:', err)
        }
      } finally {
        this.renderTask = null
      }
    }
  },
  beforeUnmount() {
    // 组件销毁前清理未完成的渲染任务
    if (this.renderTask) {
      this.renderTask.cancel()
    }
  }
}
</script>

修复关键点说明

  1. 避免并发渲染冲突:新增renderTask变量追踪当前渲染任务,每次渲染前取消上一次未完成的任务,彻底解决同一个canvas被多渲染操作占用的问题。
  2. 修正页面旋转:读取PDF页面原生的rotate属性,创建视口时传入该参数,解决页面倒置的问题。
  3. 画布清理与异常处理:渲染前清空画布避免残留内容,捕获渲染取消的异常,防止控制台出现无效报错。
  4. 组件销毁时清理:在beforeUnmount钩子中取消未完成的渲染任务,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Qiuzman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 11:25:22