html2canvas 性能优化方案有哪些?

项目中多处使用到 PDF 打印功能,我之前采用的实现方案是:

1 先在 html 上渲染出来
2 通过 html2canvas 转换为 canvas
3 通过 canvas.toDataURL 转换为 jpeg 图片 URL
4 通过 jspdf 创建 PDF 并添加转换来的 jpeg

PDF 模板中各元素尺寸是根据数据灵活变换的,考虑到纸张大小,换页,布局等问题,通过 html2canvas 将页面元素转换为 canvas 的时要根据实际情况进行多次转换生成包一个含多张图片的列表。jspdf 再根据图片列表逐一添加。

现在问题就来了,虽然功能已经实现,但 html2canvas 是异步操作,而且非常耗时,当页面只需要转换一两张图片的时候勉强还可以接受,但是当图片的数量越来越多,十张左右的时候就要二十多秒了。

为了优化这个问题,我查找并尝试了几种方法均已失败告终,方法和失败原因如下:

1 html2canvas 官网提供的 ignoreElements
根据介绍,ignoreElements 可以用来排除掉不需要被截图(转换为canvas)的 html 子元素,以便减少 html2canvas 遍历 HTML 元素的时间。但在我的项目中,因为前面提到的考虑换页等问题,已经对 html2canvas 转换的元素粒度降到最细了。没有可排除打印的子元素。

2 Promise.all 多个异步并行,
不知道是因为浏览器限制还是因为线程的资源上限,虽然在代码逻辑上多个 html2canvas 已经是并行处理了,但实际使用时间并没有减少,而且从F12的性能上看,多个 html2canvas 最终还是逐一执行的

3 web worker 后台线程处理
既然在同一个线程里没办法做到真正的并行处理,第一时间想到的就是 web worker 甚至可以使用多个 web worker 启用后台线程并行处理,但是 html2Canvas 是根据 DOM 元素进行操作处理,而 web worker 是没法访问 Dom 的,所以 html2Canvas 好像又不能放到后台线程中处理。

进行到这里就没了头绪,接下来该怎么做希望有遇到过这个问题的前辈给个方向,不胜感激。

阅读 2.7k
avatarAI BotBETA

对于 html2canvas 的性能优化,这里有一些可能的方案和建议:

  1. 减少渲染的复杂性
* 如果可能,尝试简化页面元素的复杂性,例如减少动画、阴影、复杂的 CSS 样式等。这些元素可能会增加 `html2canvas` 的渲染时间。
* 如果某些元素在打印版本中不需要,可以使用 CSS 媒体查询或 JavaScript 在转换前隐藏或删除它们。
  1. 使用 scale 选项
* `html2canvas` 允许你设置 `scale` 选项来降低渲染的分辨率。虽然这可能会降低图像的质量,但它可以显著提高渲染速度。
* 例如:`html2canvas(element, { scale: 2 })` 会将渲染的分辨率降低到原来的一半。
  1. 批量处理
* 如果可能,尝试将多个页面元素组合到一个大的 `canvas` 上,然后一次性转换为图片,而不是为每个元素单独执行此操作。这可以减少转换的次数和开销。
  1. 使用 Web Workers 处理其他任务
* 虽然 `html2canvas` 无法在 Web Worker 中运行,但你可以使用 Web Workers 来处理其他与 DOM 无关的任务,从而释放主线程并使其专注于渲染。
  1. 使用其他库或方法
* 考虑使用其他库或方法,如 `dom-to-image` 或 `puppeteer`(基于 Chrome 的无头浏览器)来进行屏幕截图。这些库可能有不同的性能特性,并可能更适合你的用例。
  1. 服务器端渲染
* 如果可能,考虑将渲染过程移动到服务器端。这样,你可以利用更强大的硬件和更少的浏览器限制来执行渲染任务。你可以使用如 Node.js 和 Puppeteer 的组合来实现这一点。
  1. 性能分析和调优
* 使用浏览器的性能分析工具(如 Chrome 的开发者工具)来识别瓶颈和性能问题。这可能会帮助你找到可以优化的特定部分或操作。
  1. 异步加载和渲染
* 如果你的页面包含大量的动态内容或异步加载的资源,考虑在转换之前确保所有内容都已完全加载和渲染。这可以避免在转换过程中发生不必要的延迟或重绘。

请注意,具体的优化策略可能因你的特定用例和项目需求而有所不同。因此,建议从上述建议开始,并根据你的具体情况进行调整和测试。

2 个回答

我目前用的方案是将 PDF 文件嵌入到页面中,然后使用浏览器的打印功能来打印页面
代码大致如下:详细代码 demo

<iframe src="" id="iframe" frameborder="0"></iframe>

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
  function loadpdf() {
    axios({
      method: 'get',
      url:
        './20200714144950101.pdf',
      responseType: 'blob',
    }).then(function (response) {
      let blob = new Blob([response.data], { type: response.data.type })
      let url = URL.createObjectURL(blob)
      document.getElementById('iframe').src = url
    })
  }
</script>

image.png


如果你的预览页面有样式要求,你可以用pdf.js去进行渲染和打印操作。
image.png
demo 源码直接右键网页源代码

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏