Skip to main content

图片快照

也叫网页截屏,其实就是将网页的某个内容导出成图片

实现方法

  1. html2canvas,可以选择纯canvasforeignObject
  2. 由 canvas 导出图像数据toBlob/toDataUrl/canvas2image
  3. 通过fileSaver.js实现图片下载

两种模式:

  • foreignObject。foreignObject 允许包含来自不同的 XML 命名空间的元素,借助 <foreignObject> 标签,我们可以直接将 DOM 节点作为 foreignObject 插入 SVG 节点中进行渲染,foreignObject 会通过解析和应用 CSS 样式表,将 HTML 内容转换为 SVG 图像,并根据 CSS 规则进行渲染。但由 x 于 SVG 和 HTML 在一些方面的差异,例如盒子模型、字体渲染等,有些 CSS 属性在 SVG 中可能会表现得不同于在 HTML 中的表现,所以极少情况下需要调整以满足需求。这其中会涉及到 html to svg 的转换,所以会有一定的性能损耗
  • 纯 canvas 实现。需要模拟 dom 结构将样式绘制到 canvas 上(parseTree),这方面 html2canvas 已经做的很不错了。但是某些动态生成的内容、使用 js 进行 DOM 操作的元素或使用 CSS 伪元素等特殊情况可能会导致 parseTree 的不准确性。此外,由于浏览器的差异性,不同浏览器对于特定 CSS 属性和布局行为的处理方式也可能会导致 parseTree 的结果有所不同,所以在特定情况下也可能需要额外的处理或调整才能满足特定需求

功能细节

大部分情况下直接用上面的方法就行了,当然可能还会存在以下一些细节上的问题需要逐个解决

内容完整性

  • 跨域问题:存在跨域图片污染 canvas 画布
  • 资源加载:生成快照时,相关资源还未加载完毕
  • 滚动问题:页面中滚动元素存在偏移量,导致生成的快照顶部出现空白

图片清晰度

  • 用高倍画布实现,html2canvas 有提供对应选项
  • 抗锯齿优化
  • 锐化

动态调整样式

  • 在 onclone 事件中调整
  • canvas 需要借助离线 canvas 动态修改。如果需要借助 echarts 或者其他库,还需要依赖它们先初始化离线 canvas 节点再进行操作

怎么隐藏节点?

  • 在离线 dom 中动态移除或隐藏某个节点(id/class/...)
  • html2canvas 在遍历 clone 时会根据标签的某个属性来判断是否 clone 该节点,会侵入代码 实现透明背景

转换效率

  • 传入阶段
    • 减少 DOM 规模,降低 html2canvas 递归遍历的计算量。
    • 压缩图片素材本身的体积,使用 tinypng 或 ImageOptim 等工具压缩素材。
    • 如果使用了自定义字体,请使用 fontmin 工具对文字进行按需裁剪,避免动辄数兆的无效资源引入。
    • 传入合适的 scale 值以缩放 canvas 画布。通常情况下 2~3 倍就已经满足一般的场景,不必要传入过大的放大倍数
  • 导出优化
    • 选择体积更小的图片格式导出

其他

  • html2canvas 库体积太大?考虑使用 dom-to-image,利用了 foreignObject,也就是 svg 内嵌 html 结构的方式