🔔 支持功能
- dom节点导出为图片(包含图片、canvas 图表)
- 可以控制导出图片的一些属性,包括背景颜色、大小、画质等
- 包含章节导航
- 文档的换行
🔨 使用的插件
dom-to-image :实现dom 转 图片
htmlDocx:导出word文档
💡 过程
总思路可以分为3步
步骤1:把一些展示图表的dom节点变成图片
步骤2:把图片插入到dom节点前,然后删除dom节点,得到最终的dom
步骤3:利用htmlDocx导出为word文档
网上搜索把dom节点变成图片的实现方案,大多都是用 html2canvas 插件
然后就封装了以下方法,详细说明请看注释:
📄 导出word第一代代码(html2canvas + htmlDocx )
import htmlDocx from 'html-docx-js/dist/html-docx';
import html2canvas from 'html2canvas';
export function wordExport(id, fileName) {
let contentNode = document.getElementById(id);
// dom转成canvas,再转成图片
let cloneContentNode = contentNode.cloneNode(true);//克隆dom节点
const imgList = contentNode.querySelectorAll('.ly-export-card');//需要转成图片的节点
const cloneImgList = cloneContentNode.querySelectorAll('.ly-export-card');//克隆的,需要转成图片的节点
const promisesTitle = Array.from(imgList).map((tit, index) => {
return new Promise(res => {
html2canvas(tit).then(canvas => {
let url = canvas.toDataURL('image/png', 1); //这里拿到base64
let img = new Image();
img.src = url;
// 插入到clone的dom节点前面
cloneImgList[index].parentNode.insertBefore(img, cloneImgList[index]);
// 删除原有dom
cloneImgList[index].parentNode.removeChild(cloneImgList[index]);
res();
});
});
});
// 将dom副本传入插件,生成文件对象,并下载下来
return Promise.all(promisesTitle)
.then(() => {
let template = `
<!DOCTYPE html>
<html lang="en">
${document.head.outerHTML}
<body>
${cloneContentNode.outerHTML}
</body>
</html>`;
const converted = htmlDocx.asBlob(template);
saveAs(converted, `${fileName}.docx`);
})
.catch(e => {
console.log(e);
});
}
// 下载文件
export function saveAs(blob, fileName) {
const a = document.createElement('a');
const url = URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
😰 由于业务需求,这个页面有很多图表都需要转成图片,以上的写法耗时比较长,在下载过程中页面造成卡顿。于是用console.time 找出耗时的地方是 html2canvas 转成canvas的时候。
😵 看了官方文档的配置,也没有看到有加快速度的方法。
于是,我考虑再换一个插件。最后找到了dom-to-image。
换成这个插件后,导出速度从之前的13秒,缩短到3秒左右!心情瞬间愉悦了呀😆
然后立刻在github上给 dom-to-image 点了一个 ⭐
当然换成 dom-to-image 的时候也遇到了小问题,但都比较容易解决了。
比如:
1、下载的图片背景是透明的
解决方法:给要生成图片的dom节点css样式设置背景颜色为白色。或者通过domtoimage.toPng(tit,options)的options参数设置图片背景
通过options还可以调整图片的大小、画质 等。详细请看 官方文档
2、图片内容错位,有一些图片只显示了一半
解决方法:要生成图片的dom节点不要设置margin
📄 导出word第二代代码(dom-to-image+ htmlDocx )
import htmlDocx from 'html-docx-js/dist/html-docx';
import domtoimage from 'dom-to-image';
export function wordExport(id, fileName) {
let contentNode = document.getElementById(id);
// dom转成canvas,再转成图片
let cloneContentNode = contentNode.cloneNode(true);//克隆dom节点
const imgList = contentNode.querySelectorAll('.ly-export-card');//需要转成图片的节点
const cloneImgList = cloneContentNode.querySelectorAll('.ly-export-card');//克隆的,需要转成图片的节点
const promisesTitle = Array.from(imgList).map((tit, index) => {
return new Promise(res => {
domtoimage.toPng(tit).then(function(url) {
let img = new Image();
res();
img.src = url;
// 插入到clone的dom节点前面
cloneImgList[index].parentNode.insertBefore(img, cloneImgList[index]);
// 删除原有dom
cloneImgList[index].parentNode.removeChild(cloneImgList[index]);
});
});
});
// 将dom副本传入插件,生成文件对象,并下载下来
return Promise.all(promisesTitle)
.then(() => {
let template = `
<!DOCTYPE html>
<html lang="en">
${document.head.outerHTML}
<body>
${cloneContentNode.outerHTML}
</body>
</html>`;
const converted = htmlDocx.asBlob(template);
saveAs(converted, `${fileName}.docx`);
})
.catch(e => {
console.log(e);
});
}
另外,实现了以下的功能:
- 章节导航:需要导航的文本,用标题标签h1-h6
- 文档的换行:如果两个图片或者文本要换行,可用
<span> </span>
或者 <p> </p>
❤️ 更多前端知识欢迎关注公众号交流