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

jsPDF+html2canvas生成PDF无数据/空白页技术求助

问题:html2canvas + jsPDF生成PDF异常
  • 使用html2canvas结合jsPDF生成PDF时,下载后的PDF仅显示少量占位元素(比如点),无实际数据
  • 之前尝试pdf.html()方法,生成的PDF数据出现在第9或13页,前面有大量空白页
  • 疑问:是否还应该继续使用html2canvas?

提供的代码

下载处理逻辑

const handleDownloadLocation = async () => {
    const content = await locationTemplateRef.current;

    try {
      const canvas = await html2canvas(content);
      const imgData = canvas.toDataURL("image/png");
      const pdf = new jsPDF({
        orientation: "portrait",
        unit: "px",
        format: "a4",
      });
      const width = pdf.internal.pageSize.getWidth();
      const height = (canvas.height * width) / canvas.width;
      pdf.addImage(imgData, "PNG", 0, 0, width, height);
      await pdf.save(`${location.name}.pdf`);
    } catch (error) {
      toast.error(error.message);
    }
  };

  return (
    <>
      <Button onClick={handleDownloadLocation} icon labelPosition="right">
        Download Location
        <Icon name="download" />
      </Button>
      <div style={{ fontSize: 8 }} ref={locationTemplateRef}>
        <DownloadDocument location={location} />
      </div>
    </>
  );

待转换的HTML组件

const {
    name,
    address,
    mainPhone,
    internet,
    siteContact,
    modem,
    externalIP,
    diocesanNetwork,
    support,
    hardware,
    phones,
    localRange,
    notes,
  } = location;

  return (
    <>
      <h1>{name}</h1>
      <ul>
        <li>{address}</li>
        <li>{mainPhone}</li>
        <li>{siteContact}</li>
        <li>Internet Speed: {internet}</li>
        <li>Ext. IP{externalIP}</li>
        <li>Diocesan net.{diocesanNetwork}</li>
        <li>Vendor Supp.{support}</li>
        <li>Moden Creds.{modem}</li>
        <li>Local IP Range{localRange}</li>
      </ul>

      <Table singleLine>
        <Table.Header>
          <Table.Row>
            <Table.HeaderCell>Device</Table.HeaderCell>
            <Table.HeaderCell>Location</Table.HeaderCell>
            <Table.HeaderCell>IP Addr.</Table.HeaderCell>
            <Table.HeaderCell>Serial</Table.HeaderCell>
          </Table.Row>
        </Table.Header>

        {hardware &&
          hardware.map((h) => (
            <Table.Body key={h.id}>
              <Table.Row>
                <Table.Cell>{h.device}</Table.Cell>
                <Table.Cell>{h.hardwareLocation}</Table.Cell>
                <Table.Cell>{h.ipAddress}</Table.Cell>
                <Table.Cell>{h.serial}</Table.Cell>
              </Table.Row>
            </Table.Body>
          ))}
      </Table>
      <Table singleLine>
        <Table.Header>
          <Table.Row>
            <Table.HeaderCell>Phone #</Table.HeaderCell>
            <Table.HeaderCell>Location</Table.HeaderCell>
            <Table.HeaderCell>Provider</Table.HeaderCell>
            <Table.HeaderCell>Support</Table.HeaderCell>
          </Table.Row>
        </Table.Header>

        {phones &&
          phones.map((p) => (
            <Table.Body key={p.id}>
              <Table.Row>
                <Table.Cell>{p.number}</Table.Cell>
                <Table.Cell>{p.numName}</Table.Cell>
                <Table.Cell>{p.provider}</Table.Cell>
                <Table.Cell>{p.misc}</Table.Cell>
              </Table.Row>
            </Table.Body>
          ))}
      </Table>
      <p>Notes: {notes}</p>
    </>
  );

PDF结果截图

PDF结果截图


问题分析与解决方案

1. 核心问题原因

  • ref获取逻辑错误:await locationTemplateRef.current是无效写法,ref.current是同步值,不需要await,这会导致代码逻辑异常
  • DOM未完全渲染:html2canvas调用时,目标元素的动态数据可能还未加载完成,导致捕获到空内容
  • 单位与尺寸换算问题:使用px作为jsPDF单位,容易出现尺寸适配错误;pdf.html()的空白页大多是因为CSS布局(如margin/padding过大、绝对定位)导致页面高度计算错误

2. 是否继续使用html2canvas?

如果需要保留复杂CSS样式(自定义表格、特殊排版),html2canvas仍可使用,修复问题即可;如果追求PDF可编辑性、更小体积,建议用jsPDF API手动绘制内容,或使用@react-pdf/renderer这类专业PDF库。

3. 具体修复方案

方案1:修复html2canvas逻辑

const handleDownloadLocation = async () => {
    const content = locationTemplateRef.current;
    if (!content) return;

    try {
      // 等待DOM完全渲染,避免动态数据未加载
      await new Promise(resolve => setTimeout(resolve, 100));
      
      const canvas = await html2canvas(content, {
        scale: 2, // 提高分辨率,避免PDF模糊
        useCORS: true,
        logging: false
      });
      const imgData = canvas.toDataURL("image/png");
      const pdf = new jsPDF({
        orientation: "portrait",
        unit: "mm", // 用mm做单位,适配PDF标准尺寸
        format: "a4",
      });

      const pdfWidth = pdf.internal.pageSize.getWidth();
      const pdfHeight = pdf.internal.pageSize.getHeight();
      const canvasRatio = canvas.width / canvas.height;
      const pdfRatio = pdfWidth / pdfHeight;

      // 按比例缩放图片,避免超出页面
      let imgWidth, imgHeight;
      if (canvasRatio > pdfRatio) {
        imgWidth = pdfWidth;
        imgHeight = pdfWidth / canvasRatio;
      } else {
        imgHeight = pdfHeight;
        imgWidth = pdfHeight * canvasRatio;
      }

      // 居中放置图片
      const x = (pdfWidth - imgWidth) / 2;
      const y = (pdfHeight - imgHeight) / 2;

      pdf.addImage(imgData, "PNG", x, y, imgWidth, imgHeight);
      pdf.save(`${location.name}.pdf`);
    } catch (error) {
      toast.error(error.message);
    }
  };

方案2:修复pdf.html()空白页问题

const handleDownloadLocation = async () => {
  const content = locationTemplateRef.current;
  if (!content) return;

  try {
    const pdf = new jsPDF();
    await pdf.html(content, {
      callback: (pdf) => pdf.save(`${location.name}.pdf`),
      margin: [10, 10, 10, 10], // 设置合理边距
      autoPaging: 'text', // 根据文本自动分页
      x: 0,
      y: 0
    });
  } catch (error) {
    toast.error(error.message);
  }
};

额外注意事项

  • 确保目标元素不是display: none,可以用position: absolute; left: -9999px; top: -9999px;隐藏模板
  • 如果使用组件库(如Ant Design、Semantic UI),需确保表格等组件的样式已正确加载

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 09:35:58