使用PDF.js在IE浏览器展示PDF页面顺序错乱如何修复?
修复IE浏览器中PDF.js加载大体积PDF时页面顺序错乱的问题
嘿,这个问题我之前帮人排查过,核心是异步操作的执行顺序不可控——尤其是在IE这种对异步处理比较“固执”的浏览器里,大体积PDF不同页面的加载耗时差异会把这个问题彻底放大。
为什么你的代码会出问题?
你代码里的pdf.getPage()是异步方法,返回的是Promise。在for循环里直接调用它时,循环会飞快地跑完所有pageNum,但每个getPage的回调要等页面加载完成才会执行。大PDF的页面大小、复杂度不一样,加载速度有快有慢,哪个先加载完就先渲染到页面上,自然就打乱了原本的顺序。IE的事件循环机制和现代浏览器有差异,这就导致这个问题在大PDF上表现得特别明显。
靠谱的修复方案
我们需要强制页面按顺序渲染,这里有两种兼容IE的可行方法:
方法1:用Promise链式调用(原生兼容IE)
因为IE不支持async/await,所以用Promise链式调用是最稳妥的原生方案:
if (response && response.data.Success) { var arrayBufferedPdf = base64ToArrayBuffer(response.data.Document); PDFJS.workerSrc = "./scripts/pdf.worker.js"; PDFJS.getDocument({ data: arrayBufferedPdf }).then(function (pdf) { var numPages = pdf.numPages; // 初始化一个已完成的Promise,用来串联所有渲染步骤 let renderSequence = Promise.resolve(); for (let pageNum = 1; pageNum <= numPages; pageNum++) { // 用立即执行函数保存当前循环的pageNum,避免异步回调拿错值 (function(currentPage) { renderSequence = renderSequence.then(function() { return pdf.getPage(currentPage).then(function(page) { var scale = 1.25; var viewport = page.getViewport(scale); var canvasContainer = document.getElementById('canvas_container'); var canvas = document.createElement('canvas'); canvas.height = viewport.height; canvas.width = viewport.width; canvasContainer.appendChild(canvas); var context = canvas.getContext('2d'); // 等待当前页面渲染完成,再进入下一个步骤 return page.render({ canvasContext: context, viewport: viewport }).promise; }); }); })(pageNum); } }); }
关键改动点:
- 用
renderSequence这个Promise来串联所有页面的渲染,确保上一页渲染完成后才开始下一页 - 用立即执行函数(IIFE)把当前循环的
pageNum保存到独立作用域,避免异步回调时引用到循环结束后的变量值 - 等待
page.render()的promise完成,确保页面真的渲染完了再继续
方法2:用async/await(需转译兼容IE)
如果你的项目有用Babel这类转译工具,可以用更简洁的async/await写法,逻辑一目了然:
if (response && response.data.Success) { var arrayBufferedPdf = base64ToArrayBuffer(response.data.Document); PDFJS.workerSrc = "./scripts/pdf.worker.js"; PDFJS.getDocument({ data: arrayBufferedPdf }).then(async function (pdf) { var numPages = pdf.numPages; var canvasContainer = document.getElementById('canvas_container'); for (let pageNum = 1; pageNum <= numPages; pageNum++) { // 等待当前页面加载完成 const page = await pdf.getPage(pageNum); var scale = 1.25; var viewport = page.getViewport(scale); var canvas = document.createElement('canvas'); canvas.height = viewport.height; canvas.width = viewport.width; canvasContainer.appendChild(canvas); var context = canvas.getContext('2d'); // 等待当前页面渲染完成,再进入下一次循环 await page.render({ canvasContext: context, viewport: viewport }).promise; } }); }
关键改动点:
- 给PDF文档加载完成的回调标记
async,在循环里用await强制等待异步操作完成 - 用
let声明pageNum,确保每次循环都有独立的作用域,不会出现变量污染
额外小贴士
- 尽量用较新的PDF.js版本,旧版本在IE下的兼容性问题更多
- 大体积PDF全量渲染可能会吃很多内存,IE的内存限制比较严格,要是用户体验还不好,可以考虑做按需渲染——只渲染当前可见区域的页面,能大幅提升性能
内容的提问来源于stack exchange,提问作者Jack philip
相关产品推荐
相关产品推荐

