html2canvas 完全实战指南:基于 DOM 的浏览器端截图方案

文章目录
微信公众号二维码
本文已同步发布到微信公众号「人言兑
👈 扫描二维码关注,第一时间获取更新!

项目做完了,用到了html2canvas,虽然代码是AI写的,但是还是碰到了很多不好解决的问题,不过总归是解决了,现在回过头静下心来认真学习一下 html2canvas,以及总结一下各类问题的解决方法。

html2canvas使用指南

一、html2canvas项目概述与核心定位

html2canvas 是一个 JavaScript HTML 渲染器,允许直接在用户浏览器中对网页或页面局部进行"截图"。该库由 Niklas von Hertzen 开发并维护,整个图像在客户端浏览器中生成,不需要服务器端渲染支持。

核心定位:基于 DOM 和样式信息构建页面表示,而非执行真正的屏幕截图。因此,其结果可能与真实视觉呈现存在差异。

项目源码托管于 GitHub:

  • 仓库地址:https://github.com/niklasvh/html2canvas
  • 克隆地址git clone git://github.com/niklasvh/html2canvas.git

基本示例

以下代码展示了最简使用方式,将 ID 为 capture 的 DOM 元素渲染为 canvas 并追加到页面中:

<div id="capture" style="padding: 10px; background: #f5da55">
  <h4 style="color: #000;">Hello world!</h4>
</div>
html2canvas(document.querySelector("#capture")).then((canvas) => {
  document.body.appendChild(canvas);
});

该库经过 gzip 压缩后体积约为 45KB,可通过 npm 或 Yarn 安装,也可直接下载 html2canvas.jshtml2canvas.min.js 使用。

二、html2canvas的安装与引入

通过 npm 安装

npm install --save html2canvas
import html2canvas from "html2canvas";

通过 Yarn 安装

yarn add html2canvas

浏览器直接引入

可直接下载 html2canvas.jshtml2canvas.min.js 文件通过 <script> 标签引入。

三、html2canvas的工作原理

html2canvas 的工作机制并非传统意义上的截图,而是一个 DOM 遍历与重建过程:

  1. DOM 遍历:脚本遍历当前页面加载的 DOM 树,收集所有元素及其应用样式;
  2. 信息提取:读取元素的样式属性,构建页面的内部表示;
  3. Canvas 绘制:基于收集到的信息在 <canvas> 上绘制图像。

注意注意! 由于每个 CSS 属性都需要手动编码支持,html2canvas 永远无法实现完整的 CSS 支持,仅覆盖最常用的属性,所有会出现生成的截图和元素DOM样式不一样的情况。

四、html2canvas实用场景与实战案例

场景 1:社交媒体分享海报生成

这是 html2canvas 最常见的使用场景之一。将页面中的海报 DOM 区域转换为图片,供用户长按保存或分享。

async function generateSharePoster() {
  const posterElement = document.querySelector(".poster-container");

  const canvas = await html2canvas(posterElement, {
    scale: 2, // 高清输出
    useCORS: true, // 允许跨域图片
    backgroundColor: null, // 透明背景
    logging: false, // 关闭调试日志
  });

  // 转换为图片并触发下载
  const imgData = canvas.toDataURL("image/png");
  const link = document.createElement("a");
  link.href = imgData;
  link.download = "share-poster.png";
  link.click();
}

关键技巧

  • 海报中的头像、背景图等素材需确保跨域可访问,或配置 useCORS: true
  • 若海报在页面上默认隐藏(如 display: none),需在 onclone 回调中将其设为可见。

场景 2:网页内容导出为 PDF

结合 jsPDF 库,将网页内容导出为 PDF 文档,适用于报表、发票、简历等场景。

import html2canvas from "html2canvas";
import jsPDF from "jspdf";

async function exportToPDF() {
  const element = document.getElementById("print-area");

  const canvas = await html2canvas(element, {
    scale: 2,
    useCORS: true,
    onclone: (doc) => {
      // 在克隆文档中隐藏打印按钮,不影响原始页面
      const printBtn = doc.getElementById("print-button");
      if (printBtn) printBtn.style.visibility = "hidden";
    },
  });

  const imgData = canvas.toDataURL("image/png");
  const pdf = new jsPDF("p", "mm", "a4");

  const imgWidth = 210; // A4 宽度 mm
  const imgHeight = (canvas.height * imgWidth) / canvas.width;

  pdf.addImage(imgData, "PNG", 0, 0, imgWidth, imgHeight);
  pdf.save("document.pdf");
}

分页处理长页面:当内容高度超过单页 A4 时,需要计算分页位置并逐页添加。

function generateMultiPagePDF(element) {
  return html2canvas(element, { scale: 2 }).then((canvas) => {
    const imgData = canvas.toDataURL("image/png");
    const pdf = new jsPDF("p", "mm", "a4");

    const pdfWidth = pdf.internal.pageSize.getWidth();
    const pdfHeight = pdf.internal.pageSize.getHeight();
    const imgWidth = canvas.width;
    const imgHeight = canvas.height;
    const ratio = Math.min(pdfWidth / imgWidth, pdfHeight / imgHeight);

    let heightLeft = imgHeight * ratio;
    let position = 0;

    // 第一页
    pdf.addImage(
      imgData,
      "PNG",
      0,
      position,
      imgWidth * ratio,
      imgHeight * ratio,
    );
    heightLeft -= pdfHeight;

    // 后续分页
    while (heightLeft > 0) {
      position = heightLeft - imgHeight * ratio;
      pdf.addPage();
      pdf.addImage(
        imgData,
        "PNG",
        0,
        position,
        imgWidth * ratio,
        imgHeight * ratio,
      );
      heightLeft -= pdfHeight;
    }

    pdf.save("multi-page.pdf");
  });
}

场景 3:地图截图与数据可视化导出

在地图应用(如 OpenLayers、Leaflet)或 ECharts 图表中,用户常需要导出当前视图。

// OpenLayers 地图导出示例
map.once("rendercomplete", function () {
  const target = map.getViewport();

  html2canvas(target, {
    ignoreElements: function (element) {
      // 过滤掉地图控件(缩放按钮、版权信息等)
      return element.className.indexOf("ol-control") !== -1;
    },
    logging: false,
    useCORS: true,
  }).then(function (canvas) {
    canvas.toBlob(function (blob) {
      saveAs(blob, "map.png");
    });
  });
});

注意:html2canvas 只能截取浏览器视口中可见的内容,超出视口的部分需要额外处理。

场景 4:电子签名与水印叠加

在生成截图后,可在 canvas 上叠加水印或签名信息。

async function generateWithWatermark() {
  const element = document.querySelector(".document-content");
  const canvas = await html2canvas(element, { scale: 2 });

  const ctx = canvas.getContext("2d");

  // 添加文字水印
  ctx.font = "20px Arial";
  ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
  ctx.rotate(-Math.PI / 6);
  ctx.fillText("CONFIDENTIAL", 100, 200);

  // 保存
  const link = document.createElement("a");
  link.download = "watermarked.png";
  link.href = canvas.toDataURL();
  link.click();
}

场景 5:差异化导出(打印版与屏幕版不同内容)

利用 onclone 钩子,可以在不影响原始页面的情况下,为导出内容添加或移除元素。

html2canvas(element, {
  onclone: (clonedDoc) => {
    // 在导出版本中移除屏幕专属元素
    const screenOnly = clonedDoc.getElementsByClassName("screen-only");
    Array.from(screenOnly).forEach((el) => el.remove());

    // 在导出版本中显示打印专属元素
    const printOnly = clonedDoc.getElementsByClassName("print-only");
    Array.from(printOnly).forEach((el) => (el.style.display = "block"));
  },
}).then((canvas) => {
  // 处理导出...
});

五、html2canvas配置选项详解与深度用法

html2canvas 提供丰富的配置选项以控制渲染行为,完整列表如下:

配置项默认值说明
allowTaintfalse是否允许跨域图像污染 canvas
backgroundColor#ffffffcanvas 背景色;若 DOM 中未指定则使用此值,设为 null 可透明
canvasnull用作绘制基础的现有 <canvas> 元素
foreignObjectRenderingfalse浏览器支持时是否使用 ForeignObject 渲染
imageTimeout15000图像加载超时(毫秒),设为 0 禁用超时
ignoreElements(element) => false断言函数,返回 true 的元素将从渲染中移除
loggingtrue是否启用调试日志
onclonenull文档被克隆用于渲染时的回调函数,可修改渲染内容而不影响原始源文档
proxynull用于加载跨域图像的代理 URL;留空则不加载跨域图像
removeContainertrue是否清理 html2canvas 临时创建的克隆 DOM 元素
scalewindow.devicePixelRatio渲染缩放比例,默认为浏览器设备像素比
useCORSfalse是否尝试使用 CORS 从服务器加载图像
width元素宽度canvas 宽度
height元素高度canvas 高度
x元素 x 偏移canvas 裁剪 x 坐标
y元素 y 偏移canvas 裁剪 y 坐标
scrollX元素 scrollX渲染元素时使用的 x 滚动位置(例如元素使用 position: fixed 时)
scrollY元素 scrollY渲染元素时使用的 y 滚动位置
windowWidthWindow.innerWidth渲染时使用的窗口宽度,可能影响媒体查询
windowHeightWindow.innerHeight渲染时使用的窗口高度

5.1 scale:清晰度控制的核心参数

scale 是控制输出质量最关键的参数。默认值为 window.devicePixelRatio(Retina 屏通常为 2,普通屏为 1)。

问题:在高清屏上若未正确处理 DPR,生成的图片会显得模糊。

解决方案

// 方案 1:显式设置 scale(推荐)
html2canvas(element, {
  scale: 2, // 或 3,根据需求平衡清晰度与性能
});

// 方案 2:使用自定义 canvas 实现更精细控制
const scaleBy = 2;
const canvas = document.createElement("canvas");
canvas.width = element.offsetWidth * scaleBy;
canvas.height = element.offsetHeight * scaleBy;
canvas.style.width = element.offsetWidth + "px";
canvas.style.height = element.offsetHeight + "px";

const ctx = canvas.getContext("2d");
ctx.scale(scaleBy, scaleBy);

html2canvas(element, {
  canvas: canvas,
  scale: 1, // 使用外部 canvas 时,内部 scale 设为 1
}).then((canvas) => {
  // canvas 已经是高分辨率输出
});

性能权衡

Scale 值输出质量内存占用适用场景
1普通快速预览
2清晰大多数生产环境
3+极清打印级 PDF、高 PPI 设备

注意:scale 提升会按平方级增加 Canvas 像素总量(宽度 × 高度 × scale²),可能导致内存溢出,尤其是在移动端。

5.2 ignoreElementsdata-html2canvas-ignore:元素过滤

用于排除不需要渲染的元素,如导航栏、广告、操作按钮等。

方式 1:使用属性(推荐,性能更好)

<div data-html2canvas-ignore>这段内容不会出现在截图中</div>

方式 2:使用函数过滤

html2canvas(document.body, {
  ignoreElements: (element) => {
    // 排除特定类名的元素
    return (
      element.classList.contains("no-export") ||
      element.classList.contains("ad-banner") ||
      element.tagName === "BUTTON"
    );
  },
});

实际案例:在地图截图中过滤控件元素。

html2canvas(document.getElementById("map"), {
  ignoreElements: (el) =>
    el.classList.contains("gmnoprint") || // Google Maps 打印控件
    el.classList.contains("gm-style-cc") || // 版权信息
    el.tagName === "BUTTON",
});

5.3 onclone:DOM 预处理神器

onclone 在文档克隆完成后、渲染开始前触发,是修改渲染内容而不影响原始页面的唯一安全时机

常见用法

html2canvas(document.body, {
  onclone: (clonedDoc) => {
    // 1. 移除临时元素
    const banner = clonedDoc.querySelector(".temporary-banner");
    if (banner) banner.parentNode.removeChild(banner);

    // 2. 修改文本内容
    const title = clonedDoc.querySelector("h1");
    if (title) title.textContent = "截图专用标题";

    // 3. 调整样式
    const chart = clonedDoc.querySelector(".echarts-container");
    if (chart) {
      chart.style.transform = "scale(1)"; // 取消缩放
      chart.style.width = "800px"; // 固定宽度
    }

    // 4. 显示隐藏元素
    const hidden = clonedDoc.querySelector(".hidden-for-export");
    if (hidden) hidden.style.display = "block";

    // 5. 调试技巧:将克隆文档挂载到全局以便在 DevTools 中检查
    window.clonedDoc = clonedDoc;
  },
}).then((canvas) => {
  document.body.appendChild(canvas);
});

重要注意onclone 中修改的是克隆文档,原始页面 DOM 完全不受影响。

5.4 useCORSallowTaint:跨域图像处理

这是最容易混淆的两个配置项,必须正确理解其区别:

配置项作用适用场景
useCORS: true尝试通过 CORS 加载跨域图片(给 <img> 添加 crossOrigin="anonymous"图片服务器支持 CORS 响应头
allowTaint: true允许跨域图片渲染到 canvas,但会污染 canvas仅查看,不需要导出图片
两者同时 trueuseCORS 优先,若 CORS 失败则回退到 allowTaint不推荐,可能导致不一致行为

正确配置组合

// 场景 A:需要导出图片,图片服务器支持 CORS
html2canvas(element, {
  useCORS: true,
  allowTaint: false, // 或不设置(默认 false)
});

// 场景 B:仅展示截图,不需要导出
html2canvas(element, {
  allowTaint: true, // canvas 被污染,无法 toDataURL()
});

// 场景 C:图片服务器不支持 CORS,但需要导出
// 必须使用代理服务器
html2canvas(element, {
  proxy: "https://your-proxy-server.com/proxy",
});

图片元素要求:使用 useCORS 时,需确保 <img> 标签也设置 crossorigin 属性。

<img crossorigin="anonymous" src="https://cdn.example.com/image.jpg" />

5.5 foreignObjectRendering:SVG 渲染模式

当设为 true 时,html2canvas 会利用浏览器的 ForeignObject 特性来渲染 SVG,可能提升某些场景下的渲染质量,但存在已知问题:

  • 不支持连字字体(如 Google Material Icons 可能显示为原始文本而非图标)
  • 不支持 opacity: 0 的元素捕获
  • 速度可能比默认模式更慢

建议:仅在特定 SVG 渲染问题无法解决时尝试开启。

5.6 scrollX / scrollY / windowWidth / windowHeight:处理固定定位与滚动

当截图元素使用了 position: fixed 或页面存在滚动时,需要正确设置这些参数。

// 截图前将页面滚动到顶部,避免截断
window.scrollTo(0, 0);
document.body.scrollTop = document.documentElement.scrollTop = 0;

html2canvas(element, {
  scrollX: 0,
  scrollY: 0,
  windowWidth: document.documentElement.scrollWidth,
  windowHeight: document.documentElement.scrollHeight,
});

5.7 排除特定元素

如需排除某些元素不参与渲染,可为这些元素添加 data-html2canvas-ignore 属性,html2canvas 将自动跳过它们。

六、html2canvas支持的 CSS 特性

html2canvas已支持的 CSS 属性

以下属性已被 html2canvas 支持:

  • background
    • background-clip不支持 text
    • background-color
    • background-imageurl()linear-gradient()radial-gradient()
    • background-origin
    • background-position
    • background-size
  • border
    • border-color
    • border-radius
    • border-style
    • border-width
  • 布局与盒模型bottombox-sizingcontentdisplayflexfloatheightleftmarginmax-heightmax-widthmin-heightmin-widthpaddingpositionrighttopwidthz-index
  • 字体与文本colorfont-familyfont-sizefont-stylefont-variantfont-weightletter-spacingline-breaktext-aligntext-decoration(含 text-decoration-colortext-decoration-linetext-decoration-style 仅支持 solid)、text-shadowtext-transformwhite-spaceword-breakword-spacingword-wrapoverflow-wrapwebkit-text-stroke
  • 列表list-style-imagelist-style-positionlist-style-type
  • 其他opacityoverflowpaint-ordertransform有限支持)、visibility

不支持的 CSS 属性

以下属性目前不支持

  • background-blend-mode
  • border-image
  • box-decoration-break
  • box-shadow
  • filter
  • font-variant-ligatures
  • mix-blend-mode
  • object-fit
  • repeating-linear-gradient()
  • writing-mode
  • zoom

七、跨域图像与代理

同源策略限制

html2canvas 无法绕过浏览器的内容安全策略限制。绘制来自当前页面源(origin)之外的图像会污染(taint)canvas,被污染的 canvas 将无法再被读取。因此,html2canvas 会在应用图像前检查其是否会污染 canvas。若 allowTaint 设为 false,跨域图像将不会被绘制。

解决方案:代理

如需加载跨域图像,可使用代理服务器将图像转发到与页面相同的源。官方目前提供以下代理实现:

  • node.js 代理

通过配置 proxy 选项指向代理服务地址即可启用。

八、浏览器兼容性

html2canvas 支持以下浏览器(需 Promise polyfill):

  • Firefox 3.5+
  • Google Chrome
  • Opera 12+
  • IE9+
  • Edge
  • Safari 6+

由于库的实现严重依赖浏览器环境,不适合在 Node.js 中使用

九、常见问题与解决方案

9.1 图像模糊与分辨率问题

现象:在 Retina 屏幕或高 DPI 设备上,生成的图片模糊不清。

原因:html2canvas 默认使用 window.devicePixelRatio 作为 scale,但在某些情况下未能正确识别或应用。

解决方案

// 显式设置 scale
html2canvas(element, {
  scale: 2, // 或 window.devicePixelRatio
  useCORS: true,
  logging: false,
});

// 对于打印级 PDF,可设置更高 scale
html2canvas(element, {
  scale: 3,
  dpi: 192, // 可选,明确设置每英寸点数
});

注意scale 提升会线性增加内存占用,移动端需谨慎使用。

9.2 跨域图片无法显示 / 空白

现象:截图中跨域图片显示为空白,控制台报错。

解决方案

  1. 首选方案:确保图片服务器返回 Access-Control-Allow-Origin 响应头,并配置 useCORS: true
  2. 备选方案:使用代理服务器转发图片;
  3. 不推荐allowTaint: true 仅适用于不需要导出图片的场景。
// 正确配置
html2canvas(element, {
  useCORS: true,
  allowTaint: false,
});

// 同时给 img 标签添加 crossorigin 属性
// <img crossorigin="anonymous" src="..." />

9.3 iOS 15 系统字体导致渲染失败

现象:在 iOS 15 设备上,html2canvas 生成空白图片或崩溃。

原因:iOS 15 中 -apple-system 字体会导致 canvas 渲染异常。

解决方案:在根节点显式声明字体,避免使用 -apple-system

#app {
  font-family:
    Helvetica, Tahoma, Arial, "PingFang SC", "Hiragino Sans GB", "Heiti SC",
    STXihei, "Microsoft YaHei", SimHei;
}

9.4 text-overflow: ellipsis 不生效

现象:CSS 中设置了 text-overflow: ellipsis,但生成的图片中文字未被截断显示省略号。

原因:html2canvas 对该属性的支持有限。

解决方案

  • 手动截断文本,使用 JavaScript 计算字符长度并添加 ...
  • 或使用固定宽度的容器配合 overflow: hidden

9.5 transform: scale() 导致文字重叠

现象:父元素设置了 transform: scale(),截图中文字重叠或错位。

原因:html2canvas 在处理缩放变换时,字体大小未按比例调整。

解决方案:在 onclone 中将缩放恢复为 1,或手动调整字体大小。

html2canvas(element, {
  onclone: function (documentClone) {
    const box = documentClone.getElementById("export-box");
    if (box) box.style.transform = "scale(1)";
  },
});

9.6 SVG 图标通过 <use> 引用时不显示

现象:使用 <symbol> + <use> 方式引用的 SVG 图标在截图中消失。

原因:html2canvas 单独解析遇到的 <svg> 元素,无法处理 <use> 的外部引用。

解决方案:在 onclone 中将 <use> 替换为内联 SVG 内容。

html2canvas(element, {
  onclone: (doc) => {
    const uses = doc.querySelectorAll("use");
    uses.forEach((use) => {
      const symbolId =
        use.getAttribute("href") || use.getAttribute("xlink:href");
      const symbol = doc.querySelector(symbolId);
      if (symbol) {
        const svg = document.createElementNS(
          "http://www.w3.org/2000/svg",
          "svg",
        );
        svg.innerHTML = symbol.innerHTML;
        use.parentNode.replaceChild(svg, use);
      }
    });
  },
});

9.7 字体加载失败导致文字错位

现象:自定义字体(如 Google Fonts、阿里巴巴普惠体)未正确加载,截图中使用回退字体,导致排版错乱。

原因:html2canvas 在渲染时会重新下载字体,若字体未缓存或网络较慢,会使用 fallback 字体。

解决方案

  • 确保字体在调用 html2canvas 前已完全加载;
  • 使用 document.fonts.ready 等待字体加载完成;
  • 将字体文件转为 Base64 内联到 CSS 中。
await document.fonts.ready; // 等待所有字体加载完成
const canvas = await html2canvas(element, { scale: 2 });

9.8 Tailwind CSS v4 oklch() 颜色解析失败

现象:使用 Tailwind CSS v4 时,html2canvas 报错 Attempting to parse an unsupported color function "oklch"

原因:Tailwind v4 默认使用 oklch() 颜色格式,html2canvas 1.4.1 尚未支持。

临时解决方案

  • 在 Tailwind 配置中回退到传统颜色格式;
  • 或等待官方支持 oklch()oklab() 的更新。

9.9 阿拉伯语 / 希伯来语等 RTL 文字渲染错乱

现象:RTL(从右到左)语言文本字符纠缠、方向错误。

原因:html2canvas 对 Unicode 双向文本算法(Bidi)的支持不完善。

解决方案

  • 手动设置文本方向;
  • 或使用专门的 RTL 处理库预处理文本。

9.10 截图被截断 / 只显示部分页面

现象:长页面截图只显示了视口内的内容,下方内容被截断。

原因:html2canvas 默认只渲染传入元素的可见区域。

解决方案

// 方案 1:设置窗口尺寸为元素完整尺寸
html2canvas(element, {
  windowWidth: element.scrollWidth,
  windowHeight: element.scrollHeight,
  scrollX: 0,
  scrollY: 0,
});

// 方案 2:截图前将页面滚动到顶部
window.scrollTo(0, 0);
document.body.scrollTop = document.documentElement.scrollTop = 0;

9.11 变量字体(Variable Fonts)font-variation-settings 不支持

现象:使用变量字体的自定义轴设置时,html2canvas 只渲染默认字重。

原因:html2canvas 尚未实现对 font-variation-settings 的解析。

解决方案:使用标准字体族替代变量字体,或预先将文字转为图片。

十、PDF 生成专题:深度问题与解决方案

10.1 字体颜色不一致 / CSS 样式异常

现象:使用 html2canvas + jsPDF 生成 PDF 时,部分文字颜色与页面显示不一致,或某些 CSS 样式丢失。

原因:html2canvas 在克隆 DOM 时,某些动态计算的样式(如通过 JavaScript 设置的样式、CSS 变量)可能未被正确继承。

解决方案:利用 onclone 在渲染前强制修正样式。

html2canvas(element, {
  scale: 2,
  onclone: (clonedDoc) => {
    // 强制设置字体颜色,确保一致性
    const textElements = clonedDoc.querySelectorAll(
      "p, span, h1, h2, h3, h4, h5, h6",
    );
    textElements.forEach((el) => {
      // 获取计算样式并强制内联
      const computedStyle = window.getComputedStyle(el);
      el.style.color = computedStyle.color;
      el.style.fontFamily = computedStyle.fontFamily;
      el.style.fontSize = computedStyle.fontSize;
      el.style.lineHeight = computedStyle.lineHeight;
    });

    // 处理特定组件的样式异常
    const cards = clonedDoc.querySelectorAll(".info-card");
    cards.forEach((card) => {
      card.style.backgroundColor = "#ffffff";
      card.style.border = "1px solid #e0e0e0";
    });
  },
}).then((canvas) => {
  // 生成 PDF...
});

10.2 Bootstrap 进度条文字显示异常

现象:PDF / 分享图片中,Bootstrap 的 .progress-stacked 进度条上的文字换行挤在一起,或被裁剪消失。

原因分析: Bootstrap 的 .progress-stacked 依赖 overflow: hidden 来裁剪进度条色块,但 html2canvas 在将 DOM 渲染成 canvas 时,对 overflow: hidden + flexbox 的处理与浏览器不一致,导致文字被裁掉或强制换行。

此外,html2pdf.js 的 onclone 回调操作的是一个沙箱副本 DOM,而 html2canvas 实际渲染的是另一份拷贝,两者不是同一个对象,所以在 onclone 里的任何 DOM 修改都不会反映到最终渲染结果上。

解决方案:在调用 html2canvas / html2pdf 之前,直接操作页面真实 DOM,将 .progress-stacked 替换成用绝对定位实现的自定义色块+文字结构,渲染完成后再还原。绝对定位完全脱离 overflow: hidden 的影响,是 html2canvas 能稳定渲染的布局方式。

async function exportWithProgressBar(element) {
  // 1. 备份原始 HTML
  const originalHTML = element.innerHTML;

  // 2. 替换进度条为绝对定位版本(操作真实 DOM)
  const progressBars = element.querySelectorAll(".progress-stacked");
  progressBars.forEach((bar) => {
    const width = bar.style.width || "50%";
    const label = bar.querySelector(".progress-label")?.textContent || "";

    // 创建绝对定位的替代结构
    const replacement = document.createElement("div");
    replacement.className = "progress-absolute-wrapper";
    replacement.style.cssText = `
      position: relative;
      width: 100%;
      height: 20px;
      background: #e9ecef;
      border-radius: 4px;
    `;

    replacement.innerHTML = `
      <div style="
        position: absolute;
        left: 0; top: 0;
        width: ${width};
        height: 100%;
        background: #0d6efd;
        border-radius: 4px;
      "></div>
      <span style="
        position: absolute;
        left: 50%; top: 50%;
        transform: translate(-50%, -50%);
        font-size: 12px;
        color: #333;
        white-space: nowrap;
      ">${label}</span>
    `;

    bar.parentNode.replaceChild(replacement, bar);
  });

  try {
    // 3. 执行截图
    const canvas = await html2canvas(element, {
      scale: 2,
      useCORS: true,
    });

    // 4. 生成 PDF 或图片
    const imgData = canvas.toDataURL("image/png");
    // ... 使用 jsPDF 生成 PDF

    return imgData;
  } finally {
    // 5. 还原原始 DOM(无论成功失败都执行)
    element.innerHTML = originalHTML;
  }
}

关键要点

  • 不要依赖 onclone 来修改进度条结构,因为 html2pdf.js 和 html2canvas 使用的克隆文档是独立的;
  • 绝对定位是 html2canvas 最稳定的布局方式,避免使用 overflow: hidden + flexbox 的组合;
  • 操作真实 DOM 后务必备份并还原,避免影响用户界面。

十一、使用建议与注意事项

  1. 实验性状态:该脚本仍处于非常实验性的阶段,不建议在生产环境中使用或基于此构建应用,因为未来可能会有重大变更。
  2. Promise 支持:html2canvas 依赖 Promise,如需兼容旧版浏览器,请在引入 html2canvas 前加载如 es6-promise 之类的 polyfill。
  3. 插件内容:脚本不渲染插件内容,如 Flash 或 Java Applet。
  4. Tainted Canvas:页面中已有的 <canvas> 元素若被跨域内容污染,html2canvas 将无法读取它们。
  5. 性能优化:处理大型 DOM 时,建议:
    • 使用 ignoreElements 过滤非必要元素;
    • 缩小截图范围,避免 document.body
    • 关闭 logging 以减少控制台输出开销。

也可以看看