适用范围:用于将网页中特定区域的内容(通过截图)转换为图像,并在此过程中隐藏和恢复某些页面元素的可见性,以确保生成的图像不包含这些元素。用于实现类似于网页打印或下载截图的功能。
对于我来说做这个功能的目的是针对上文项目需求将不确定多少数据的表格转换为图片保存到本地,它与打印功能及其相似,只不过是以图片的格式存储下来。
先说思路:对所负责的项目需求进行了解之后,我一开始想的不是打印下载,而是做一个导出数据的功能,因为相对于打印下载,导出功能是比较简单且逻辑也不是太绕,当然这是对于我这种小菜的思维来说的,大佬们手到擒来,撒撒水啦。
通过需求评审环节,与产品沟通之后,还是确定以当前页截图下载到本地的形式来做,因为一个当前页面不会只有一个table页面展示,还有其他需要展示的列表以及图形化图表。
废话不多说,实现思路如下:
- 首先我们从按钮组件开始说起,如果说是下载功能,差不多一开始大家都会想直接从Antd引用一个Button按钮,或者自定义一个标签元素即可,然后CSS样式将按钮定位在适当的位置。本项目是利用了Tabs标签页的形式,在一个页面上展示了不同的数据面板,其实也可以利用按钮进行左右切换,做一个ID判断就可以。但对于小菜我来说,当然是有相关组件就使用相关组件,这样好处就是不用去写CSS样式,避免不必要的样式污染。tabs组件中有一个tabBarExtraContent属性,它里面定义好了Button的位置,只需在这个方法中添加一个Button按钮就可以。
- Button按钮里面定义点击事件,在事件中:
- 定义图片格式转换方法;
- 定义图片下载的方法
- 将canvas 元素转换为图像并下载保存为指定文件名的图像文件。
- 点击事件的处理,其中包括:
- 获取要隐藏的元素,如果是获取多个元素可以用querySelectorAll方式
- 如果是多个元素的获取,需要将得到的伪数组转为真数组后并遍历每个dom元素
- 通过遍历在下载前将每个需要隐藏的dom元素都隐藏
- 获取要打印截图的dom元素
- 判断dom是否存在,如果存在,代码使用
html2canvas
库来截取dom
元素的屏幕截图,将其渲染到一个新的 canvas 元素中。这个 canvas 元素存储了截图。 - 然后在调用第一步图片格式转换的方法,并将生成的 canvas 元素和文件名作为参数传递给它,以将截图转换为图像并下载。
- 最后就是在遍历一次将隐藏的dom元素显示
代码呈现:
1、定义图片格式转换方法:
这些步骤,对于小菜我来说其实是想不到的,参考了现实及网上一些其他大佬们的解答后整理的(如有侵权冒犯,请联系)。
// 打印:1、定义图片格式转换方法
// 这个函数接受一个 data的URL(dataurl)作为参数,将其转换为 Blob 对象并返回。
function dataUrltoBlob(dataurl: any) {
// 通过逗号分隔的方式将 data URL 分成两部分,提取出 MIME 类型和 base64 编码的数据部分
let arr = dataurl.split(',');
let mime = arr[0].match(/:(.*?);/)[1];
// 使用 atob 函数将 base64 数据解码成二进制字符串
let bstr = atob(arr[1]);
let n = bstr.length;
// 创建一个 Uint8Array 数组,将解码后的二进制数据存储在其中。
let u8arr = new Uint8Array(n);
while (n--) {
// 进入一个 while 循环,循环条件是 n 的值递减,循环会一直执行到 n 减为 0 为止
// 在循环内部,将二进制字符串 bstr 中每个字符的 ASCII 值存储在 u8arr 数组的相应位置
u8arr[n] = bstr.charCodeAt(n);
}
// 返回一个新的 Blob 对象,该对象包含了存储在 u8arr 数组中的二进制数据,并具有指定的 MIME 类型
return new Blob([u8arr], { type: mime });
}
2、图片下载
// 打印:图片下载
// 定义名为 downImg 的函数,该函数接受一个参数 myUrl。
function downImg(myUrl: any) {
// 创建一个新的 <a> 元素,并将其存储在名为 printWindow 的常量中
const printWindow = document.createElement('a');
// 设置 printWindow 元素的 href 属性为传入的 myUrl,以指定下载链接
printWindow.href = myUrl;
// 使用 setAttribute 方法将 target 属性设置为 '_blank',以在新标签页中打开链接。
printWindow.setAttribute('target', '_blank');
// 使用 setAttribute 方法将 downLoad 属性设置为 '图片名称',以指定下载的文件名
printWindow.setAttribute('downLoad', '下载后图片的默认名称');
// 调用 click 方法触发下载操作
printWindow.click();
}
const convertCanvasToImg = (canvas: any, filename: any) => {
// 调用之前定义的 dataUrltoBlob 函数,将 canvas 元素转换为 Blob 对象,并将结果存储在名为 myBlob 的常量中。图片效果为0.8倍
const myBlob = dataUrltoBlob(canvas.toDataURL('img/png', 0.8));
// 使用 URL.createObjectURL 创建一个对象 URL (myUrl),该 URL 可用于下载 Blob 对象。
const myUrl = URL.createObjectURL(myBlob);
// 调用之前定义的 downImg 函数进行图片下载。
downImg(myUrl);
};
3、Button按钮点击事件执行打印功能
// 点击打印功能执行
const projectBordPrint = () => {
// 声明一个名为 domPagination 的变量,并将其初始化为 null。
let domPagination: HTMLElement = null;
// 获取所有的隐藏的元素,并将结果存储在 domPagination 变量中。使用 as 关键字将结果断言为 HTMLElement 类型。
domPagination = document.querySelectorAll(
'.类名'
) as unknown as HTMLElement;
// 转为真数组去遍历每个dom元素
let domPaginationT = Array.from(domPagination);
// 遍历 domPaginationT 数组中的每个元素,将下载前每个需要隐藏的dom元素隐藏
domPaginationT.forEach((item: any) => {
return (item.style.display = 'none');
});
let dom: HTMLElement = null;
// 获取需要打印的dom元素
dom = document.querySelector('#id名') as HTMLElement;
if (dom) {
// 使用 html2canvas 库捕获 dom 元素的屏幕截图并将其转换为canvas。一旦生成canvas,就调用 convertCanvasToImg 函数将其转换为图片。
html2canvas(dom, { scale: 1, useCORS: true }).then(function (canvas) {
convertCanvasToImg(canvas, '文件名');
});
}
// 下载完的时候每个dom元素显示
domPaginationT.forEach((item: any) => {
//我这边是因为设置了dispaly:flex布局
return (item.style.display = 'flex');
});
};
关于TS查询多个元素时as unknown断言的注释:
as unknown
和as HTMLElement
是类型断言(Type Assertion)的语法。它们用于告诉 TypeScript 编译器如何处理查询到的DOM元素集合,以满足代码中的类型检查。具体来说,
document.querySelectorAll('.类名')
返回的是一个NodeList
类型的元素集合,但是我是想将这个元素集合明确地类型转换为HTMLElement
类型的数组。然而呢,TypeScript 会在此处产生一个类型错误,因为NodeList
不是HTMLElement
的直接子类型。因此,为了解决这个问题,在这边使用两次类型断言:
as unknown
: 这部分将NodeList
类型断言为unknown
类型,相当于告诉 TypeScript 编译器,我知道这里的类型可能是什么,但我暂时将它视为未知类型。
as HTMLElement
: 接着,我们将unknown
类型再次断言为HTMLElement
类型,这个操作是显式告诉 TypeScript 编译器,我确定现在的类型是HTMLElement
。这样做的目的是为了满足 TypeScript 的类型检查,同时确保在代码中使用
domPagination
时,它被视为HTMLElement
类型的数组,从而可以访问HTMLElement
的属性和方法。但请注意,使用这种方式需要确保实际的 DOM 结构和选择器的匹配,以免在运行时出现类型错误。
做到这,基本上是可以完成点击下载将当前某个区域的数据截图转换成图片的形式啦!
此外,该文章是在项目中总结,期间百度查询、CSDN查询、同事帮助等方式都有,没有搬抄,如有不足之处,请指正,谢谢。如果代码有相似侵权冒犯之处,请联系,谢谢! 禁止转载!