react页面指定dom转pdf导出

news2024/11/15 13:58:13

      • 1. 使用jsPDF+html2canvas将页面转成图片然后导出
      • 2. 自定义创建iframe调用iframe.print()进行页面打印转pdf导出
      • 3. 使用react-to-print插件打印pdf
      • 4. 利用@media print样式打印页面局部元素

1. 使用jsPDF+html2canvas将页面转成图片然后导出

缺点:页面过长可能会导出失败,并且由于电脑分辨率的问题导致导出文件模糊不清

实现代码:

import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';

export function downToPdf(className: string, name: string) {
  let elements = document.getElementsByClassName(className); // 获取指定类名的元素
  if (elements.length === 0) {
    return;
  }
  let element: any = elements[0]; // 使用第一个匹配到的元素
  let w = element.offsetWidth; // 获取容器的宽度
  let h = element.offsetHeight; // 获取容器的高度
  let canvas = document.createElement("canvas");
  canvas.width = w;
  canvas.height = h;
  // 默认横向没有滚动条的情况,因为offsetLeft有无滚动条的时候存在差值,因此
  // translate的时候,要把这个差值去掉
  html2canvas(element, {  // 设置option可去除灰色色块
    allowTaint: false,
    useCORS: true,
    scale: 1.2, // 用于渲染的比例。默认为浏览器设备像素比率
    backgroundColor: '#F5F5F5',
    windowWidth: element.scrollWidth,
    windowHeight: element.scrollHeight
  }).then(function (canvas) {
    let contentWidth = canvas.width;
    let contentHeight = canvas.height;
    // 一页pdf显示html页面生成的canvas高度
    let pageHeight = (contentWidth / 592.28) * 841.89;
    // 未生成pdf的html页面高度
    let leftHeight = contentHeight;
    // 页面偏移
    let position = 0;
    // a4纸的尺寸[595.28,841.89]pt,html页面生成的canvas在pdf中图片的宽高
    let imgWidth = 595.28;
    let imgHeight = (592.28 / contentWidth) * contentHeight - 56.7;  // 上下边距10mm

    let pageData = canvas.toDataURL("image/jpeg", 1.0);

    let pdf = new jsPDF("p", "pt", "a4");

    // 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
    // 当内容未超过pdf一页显示的范围,无需分页
    if (leftHeight < pageHeight) {
      pdf.addImage(pageData, "JPEG", 0, 28.35, imgWidth, imgHeight);
    } else {
      // 分页
      while (leftHeight > 0) {
        pdf.addImage(pageData, "JPEG", 0, position + 28.35, imgWidth, imgHeight);
        leftHeight -= pageHeight; // 加上页面高度和上下的页面距
        position -= 841.89;
        // 避免添加空白页
        if (leftHeight > 0) {
          pdf.addPage();
        }
      }
    }
    pdf.save(name + ".pdf");
  });
}

2. 自定义创建iframe调用iframe.print()进行页面打印转pdf导出

缺点:对于echarts图表可能出现样式问题

代码实现:

export const printPDF = (dom: any) => {
  // 将canvas元素转换为图片
  convertCanvasToImages(dom);
};

// 写入iframe
function writeIframe(dom: any) {
  const iframe: any = document.createElement("iframe");
  iframe.style.position = "absolute";
  iframe.style.width = "0";
  iframe.style.height = "0";
  iframe.style.top = "-10px";
  iframe.style.left = "-10px";
  document.body.appendChild(iframe);
  const doc: any = iframe.contentDocument;

  doc.open();
  doc.write(getStyle() + getHtml(dom));
  doc.close();

  iframe.onload = function () {
    iframe.contentWindow.print();

    setTimeout(() => {
      document.body.removeChild(iframe);
    }, 100);
  };
}

// 获取样式
function getStyle() {
  const styles = document.querySelectorAll("style,link");
  let str = "";

  for (let i = 0; i < styles.length; i++) {
    str += styles[i].outerHTML;
  }
  str += `<style>
    @media print {
      html,body{
        height: auto;
        margin: 0;
      }
      body{
        zoom: 100%;
      }
      img{
        max-width: 100% !important;
        height: auto !important;
        page-break-inside: auto;
        break-inside: auto;
      }
      @page {
        margin: 0;
      }
    }
  </style>`;
  return str;
}

// 获取dom
function getHtml(dom: any) {
  return dom.outerHTML;
}

// 将canvas元素转换为图片
function convertCanvasToImages(dom: any) {
  const canvasElements = dom.querySelectorAll('canvas');
  let convertedCount = 0;

  if (canvasElements.length === 0) {
    writeIframe(dom);
    return;
  }

  canvasElements.forEach((canvas: any) => {
    const img = document.createElement('img');
    img.src = canvas.toDataURL('image/png');
    img.style.width = '100%';
    img.style.height = 'auto';
    canvas.parentNode.replaceChild(img, canvas);

    convertedCount++;
    if (convertedCount === canvasElements.length) {
      writeIframe(dom);
    }
  });
}

3. 使用react-to-print插件打印pdf

缺点:对于大量echarts图表可能会随机几个出现样式问题

代码实现:

  1. 下载: yarn add react-to-print
  2. 引入插件

import {useReactToPrint} from “react-to-print”;

  1. 使用
import React, { useRef } from 'react';
import { useReactToPrint } from 'react-to-print';

const PrintComponent = React.forwardRef((props, ref) => {
  return (
    <div ref={ref} style={{ width: '100%', height: 'auto' }}>
      {/* 你的长内容 */}
      <div>Page 1</div>
      <div>Page 2</div>
      <div>Page 3</div>
      {/* 更多内容 */}
    </div>
  );
});

const App = () => {
  const printRef = useRef();

  // 打印功能
  const handlePrint = useReactToPrint({
    content: () => printRef.current,
    pageStyle: `
      @page {
        size: A4;
        margin: 0;
      }
      @media print {
        body, html {
          height: auto;
          overflow: initial !important;  // 重要,如果不分页需要加上
          margin: 0;
        }
        body{
          zoom: 100%;
        }
        img{
          max-width: 100% !important;
          height: auto !important;
          page-break-inside: auto;
          break-inside: auto;
        }
      }`,
    removeAfterPrint: true,
    documentTitle: `报告名称`,
  });

  return (
    <div>
      <PrintComponent ref={printRef} />
      <button onClick={handlePrint}>打印</button>
    </div>
  );
};

export default App;

4. 利用@media print样式打印页面局部元素

使用该方式,不需要更改浏览器的原生打印,只需要样式控制即可打印出指定部分的内容

  1. 页面结构
    在这里插入图片描述
  2. 样式控制
@media print {
  @page {
    size: A4;
    margin: 0;
  }

  .ant-button, #zdns-header, .ant-notification {
    display: none;
  }

  .reportTemplateScene {
    border: none;
    margin: 0;
    height: auto;
    overflow: initial !important;
    page-break-inside: avoid; /* 避免分页中断 */
  }

  #root > div {
    height: auto; /* 确保内容不会被固定高度限制 */
  }

  /* 移除祖父节点的滚动条样式 */
  .ant-spin-container::-webkit-scrollbar,
  .ant-spin-container::-webkit-scrollbar-thumb,
  .ant-spin-container::-webkit-scrollbar-track {
    display: none;
  }

  /* 确保祖父节点在打印时不显示滚动条 */
  .ant-spin-container {
    overflow: hidden !important;
    padding: 0 !important;
  }

  /* 确保所有内容都可见 */
  body, html {
    overflow: visible !important;
  }

  /* 确保所有内容都打印出来 */
  * {
    visibility: visible !important;
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1937772.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SwarmBrain: 通过大模型玩实时战略游戏《星际争霸II》

人工智能咨询培训老师叶梓 转载标明出处 实时战略&#xff08;RTS&#xff09;游戏如《星际争霸II》一直被视为测试和提升AI能力的绝佳平台。尽管基于强化学习&#xff08;RL&#xff09;的AI代理在《星际争霸II》中取得了显著进展&#xff0c;但它们在处理复杂环境时仍面临挑战…

SpringBoot、SpringCloud、SpringCloud Alibaba版本对应关系

1. 概述 随着SpringBoot和SpringCloud的发展&#xff0c;有些服务进入停更运维状态&#xff0c;一些新的框架出现&#xff0c;在开发微服务的时候不同组件的版本对应关系也是不可忽视的问题&#xff0c;如果版本不对应&#xff0c;可能会出现很多莫名的错误&#xff0c;给开发…

5G mmWave PAAM 开发平台

Avnet-Fujikura-AMD 5G 毫米波相控阵天线模块开发平台 Avnet 和 Fujikura 为毫米波频段创建了一个领先的 5G FR2 相控阵天线开发平台。该平台使开发人员能够使用 AMD Xilinx 的 Zynq UltraScale™ RFSoC Gen3 和 Fujikura 的 FutureAcess™ 相控阵天线模块 (PAAM) 快速创建和制…

AI周报(7.14-7.20)

AI应用-本届欧洲杯的AI技术应用 卢卡库在两场比赛中共有三次庆祝进球的情况&#xff0c;但遗憾的是这三次庆祝均未能转化为有效的进球。这种“吐饼王”的表现也让他成为了本届欧洲杯的一个另类焦点人物。虽然稍显悲情&#xff0c;但有了AI的深度应用&#xff0c;即使这样极端的…

震惊,搜索二叉树告诉我们不要生二胎?本篇(带图)让你轻松拿下

引子&#xff1a; 二叉树作为一种常见的一种数据结构,我们也经常使用&#xff0c;有以下几种类型&#xff1a;满二叉树&#xff08;Full Binary Tree&#xff09;&#xff1a;所有节点都恰好有两个子节点或没有子节点。完全二叉树&#xff08;Complete Binary Tree&#xff09…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第三十六章 Linux驱动初探

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

Python list comprehension (列表推导式 - 列表解析式 - 列表生成式)

Python list comprehension {列表推导式 - 列表解析式 - 列表生成式} 1. Python list comprehension (列表推导式 - 列表解析式 - 列表生成式)2. Example3. ExampleReferences Python 中的列表解析式并不是用来解决全新的问题&#xff0c;只是为解决已有问题提供新的语法。 列…

易我数据恢复怎么样?这款四款也很好用

虽然我已经很小心了&#xff0c;但是生活中总有一些意外情况导致数据丢失&#xff0c;这时&#xff0c;数据恢复软件便成为了我们的救命稻草。不知道大家用过易我数据恢复没。我用过&#xff0c;但是目前更喜欢下面的四款数据恢复工具&#xff0c;更符合我的使用习惯&#xff0…

【ai】学习笔记:电影推荐1:协同过滤 TF-DF 余弦相似性

2020年之前都是用协同过滤2020年以后用深度学习、人工智能视频收费的,不完整,里面是电影推荐 这里有个视频讲解2016年大神分析了电影推荐 :MovieRecommendation github地址 看起来是基于用户的相似性和物品的相似性,向用户推荐物品: 大神的介绍: 大神的介绍: 基于Pytho…

Windows tasklist命令详解,Windows查看进程

「作者简介」&#xff1a;冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础著作 《网络安全自学教程》&#xff0c;适合基础薄弱的同学系统化的学习网络安全&#xff0c;用最短的时间掌握最核心的技术。 tasklist 可以…

Chapter14 非真实感渲染 NPR——Shader入门精要

Chapter14 非真实感渲染 NPR 一、卡通风格渲染1.渲染轮廓线2.添加高光3.ToonShadingMat 二、素描风格渲染 一、卡通风格渲染 特点&#xff1a;物体被黑色线条描边&#xff0c;分明的明暗变化等方法&#xff1a;其中之一就是 基于色调的着色技术 1.渲染轮廓线 方法 基于观察角…

零基础入门鸿蒙开发 HarmonyOS NEXT星河版开发学习

今天开始带大家零基础入门鸿蒙开发&#xff0c;也就是你没有任何编程基础的情况下就可以跟着石头哥零基础学习鸿蒙开发。 目录 一&#xff0c;为什么要学习鸿蒙 1-1&#xff0c;鸿蒙介绍 1-2&#xff0c;为什么要学习鸿蒙 1-3&#xff0c;鸿蒙各个版本介绍 1-4&#xff0…

MYSQL2

1.建库以及建表&#xff1a; mysql> create database mydb8_woker; mysql> use mydb8_woker; mysql> create table t_worker( -> department_id int(11) not null comment 部门号, -> worker_id int(11) primary key not null comment 职工号, -&…

Java中的Heap(堆)(如果想知道Java中有关堆的知识点,那么只看这一篇就足够了!)

前言&#xff1a;&#xff08;Heap&#xff09;是一种特殊的完全二叉树&#xff0c;它在诸多算法中有着广泛的应用&#xff0c;本文将详细介绍Java中的堆。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客 先让我们看一下本文大…

《Milvus Cloud向量数据库指南》——开源许可证的开放度:塑造AI开发合作与创新的双刃剑

在人工智能(AI)技术日新月异的今天,开源软件作为推动技术创新的重要力量,其许可证的开放度成为了影响AI开发合作、创新模式乃至整个行业生态的关键因素。不同的开源许可证模型,以其各自独特的开放程度,不仅决定了软件项目的可访问性和可定制性,还深刻影响着AI领域内的合…

springboot 实体类加注解校验入参数据

导入的是springboot自身的依赖包 import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.Valid;

R语言进行K折交叉验证问题

在使用R语言进行模型参数评估优化时候&#xff0c;会使用K折交叉验证&#xff0c;其中会遇到各种各样问题&#xff1a; 错误: C5.0 models require a factor outcome > (1-mean(E0));(1-mean(E1)) [1] 1 [1] 1 报错说明C5.0模型需要因子变量输出&#xff0c;源代码如下&am…

还在为电脑录屏困扰吗?试试这4个方法,瞬间解决

现在很多人用手机进行日常操作都知道录屏的功能怎么操作&#xff0c;但是电脑录屏怎么录呢&#xff1f;如果你需要使用电脑进行录屏操作的时候就可以看看这篇文章。 1.福晰录屏大师 这个工具是一个专业的录屏软件。可以控制录制的区域范围&#xff0c;也能控制音频来源&#…

鸿蒙仓颉语言【匹配match】

模式匹配match match特性是现代编程语言中常见的特性&#xff0c;它们在不同的编程语言中有类似的概念和语法&#xff0c;但在细节上可能有一些差异。它们都可以提高代码的灵活性和可重用性&#xff0c;但用法和语法可能会因编程语言而异。 仓颉的match 支持通过箭头函数直接…

python用selenium网页模拟时xpath无法定位元素解决方法2

有时我们在使用python selenium xpath时&#xff0c;无法定位元素&#xff0c;红字显示no such element。上一篇文章写了1种情况&#xff0c;是包含iframe的&#xff0c;详见https://blog.csdn.net/Sixth5/article/details/140342929。 本篇写第2种情况&#xff0c;就是xpath定…