前端方案/导出DOM元素为图片或PDF(类海报)

10/11/2024 Vue

在前端开发中,偶尔会遇到导出页面为图片(或海报)或者PDF这种常见需求,大多数是直接获取DOM,然后通过htm2canvas库(导出pdf的话还需要加上pdf.js库)导出,但是大部分直接导出样式总会出点问题(比如行高不对,样式错乱等),另外想在导出的页面中加一些图片或者其他元素,有些不方便。所以我这边思路是重新开一个导出组件,用来放要导出的元素,以及要额外放的一些内容(也就是在保留原有页面的基础上,基于业务数据新写一个...);下面使用htm2canvas导出图片,使用pdf.js导出为pdf

# 效果展示:需要导出的页面

在这里插入图片描述

# 效果展示:导出效果预览

在这里插入图片描述

# 基于以下两个库

import html2canvas from 'html2canvas';
import JsPDF from 'jspdf';
1
2

# 以下函数共三个功能,复制图片、导出为图片、导出为pdf(可分页)

其中downloadImgAndPdf函数的入参是1、2、3,分别代表复制图片、下载图片、下载pdf

     downloadImgAndPdf(val) {
         let excludeSelectors = ['.screenBtn', '.copyBtn', '.copyBtnuser', '.copycodebtn'];// 需要排除的元素选择器,这些元素不会出现在导出的图片中
         const divIds = ['scrollBox'];// 需要截图的元素id
         const pdf = new JsPDF('p', 'mm', 'a4');// A4纸,纵向
         const captureScrollableContent = async (divId) => {
             const originalDiv = document.getElementById(divId);
             // 创建克隆节点
             const clonedDiv = originalDiv.cloneNode(true);
             document.body.appendChild(clonedDiv);
             // 设置克隆节点样式
             clonedDiv.style.position = 'absolute';
             clonedDiv.style.left = '-9999px';
             clonedDiv.style.height = 'auto';
             clonedDiv.style.width = '840px'; // 设置固定宽度
             clonedDiv.style.overflow = 'visible';
             // 隐藏克隆节点中需要排除的元素
             excludeSelectors.forEach(selector => {
                 clonedDiv.querySelectorAll(selector).forEach(el => {
                     el.style.display = 'none';
                 });
             });
             try {
                 const canvas = await html2canvas(clonedDiv, {
                     allowTaint: false,
                     taintTest: false,
                     logging: false,
                     useCORS: true,
                     width: 840,
                     scale: 2,
                 });

                 return canvas;
             } finally {
                 // 移除克隆节点
                 document.body.removeChild(clonedDiv);
             }
         };
         const promises = divIds.map(captureScrollableContent);
         Promise.all(promises).then(canvases => {
             canvases.forEach((canvas, index) => {
                 if (val === '1') {//val为1是复制图片
                     // 生成图片URL
                     const imageData = canvas.toDataURL("image/png");
                     // 创建一个隐藏的a标签用于下载
                     // 将 base64 转换为 Blob 对象
                     fetch(imageData)
                         .then(res => res.blob())
                         .then(blob => {
                             // 创建一个新的剪贴板项目
                             const item = new ClipboardItem({ "image/png": blob });
                             // 使用 Clipboard API 复制到剪贴板
                             navigator.clipboard.write([item]).then(() => {
                                 this.$Message.success("复制成功")
                             }).catch(err => {
                                 this.$Message.error("复制失败")
                             });
                         });
                 } else if (val === '2') { //val为2是下载图片
                     // 生成图片URL
                     const imageData = canvas.toDataURL("image/png");
                     // 创建一个隐藏的a标签用于下载
                     const link = document.createElement('a');
                     link.href = imageData;
                     link.download = `image_${index + 1}.png`; // 设置下载的文件名
                     // 模拟点击下载
                     link.click();
                 } else { //val为3是下载pdf
                     // 生成图片URL
                     const imageData = canvas.toDataURL("image/png");
                     // 创建一个隐藏的a标签用于下载
                     const link = document.createElement('a');
                     link.href = imageData;
                     link.download = `image_${index + 1}.png`; // 设置下载的文件名
                     if (index > 0) {
                         pdf.addPage();
                     }
                     this.addCanvasToPdf(pdf, canvas);
                     pdf.save('人工智能平台.pdf');
                 }
             });

         }).catch(error => {
             console.error('Error generating images:', error);
         });
     },
     addCanvasToPdf(pdf, canvas) {//添加canvas到pdf
         var ctx = canvas.getContext('2d');// 获取画布的2D上下文
         var a4w = 190; var a4h = 277; // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
         var imgHeight = Math.floor(a4h * canvas.width / a4w); // 按A4显示比例换算一页图像的像素高度
         var renderedHeight = 0;

         while (renderedHeight < canvas.height) {
             var page = document.createElement('canvas');
             page.width = canvas.width;
             page.height = Math.min(imgHeight, canvas.height - renderedHeight);// 可能内容不足一页

             // 用getImageData剪裁指定区域,并画到前面创建的canvas对象中
             page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0);
             pdf.addImage(page.toDataURL('image/jpeg', 1.0), 'JPEG', 10, 10, a4w, Math.min(a4h, a4w * page.height / page.width)); // 添加图像到页面,保留10mm边距

             renderedHeight += imgHeight;
             if (renderedHeight < canvas.height) {
                 pdf.addPage();// 如果后面还有内容,添加一个空页
             }
         }
     },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
最后更新于: 2024年10月10日星期四中午12点07分