主题
平台关于html2canvas插件的使用问题
问题描述
- 平台提交审批时需要截取页面长图,提供审批内容明细。截图时,页面等待时间长,且出现提示登录超时,跳转至登录页的问题。
- 经排查发现截图时出现项目资源文件重新加载和某些页面created、mounted等钩子函数被执行的情况。这里就因为执行了login.vue的created钩子函数。导致token被清空,上传图片时请求接口失败。
问题原因
- 这里截图功能使用html2canvas插件,HTML2Canvas是一种JavaScript库,它将HTML、CSS以及可能的部分JavaScript内容转换为图片(Canvas元素)。
- 由于html2canvas从文档的根节点开始,递归地遍历页面上的所有可见元素,包括文本、图像、表格、div等, 构建一个dom树,然后遍历dom树,将每个节点转换为canvas元素。 这个过程中相当于克隆了一份网页的副本(包括了预加载页面和静态资源)在浏览器上,耗时也比较长。且页面实例被创建时,vue的生命周期钩子函数会被执行,导致上述问题。
解决方案
提高截图生成效率、避免重复加载静态资源文件以及其他页面生命周期函数被执行的方法如下:
- 按钮增加loading效果,避免重复点击,导致截图函数被重复执行。
- 过滤节点,html2canvas的配置项提供了一个回调函数 ignoreElements, 他的作用就是过滤节点的,减少遍历的节点以及克隆,提高效率。
参考链接
- 关于html2canvas插件实现原理相关知识(https://blog.csdn.net/weixin_43502666/article/details/136902310)
实现代码
- 就以html2canvas插件实现得审批截图功能为例。
vue
<template>
<el-dialog title="审批" :visible.sync="dialogVisible" v-dialog-loading="pageLoad" width="45em" center @closed="closed" :close-on-click-modal="false">
<el-form :model="form" :rules="rules" ref="formRef" label-width="7em" id="print-cost-form">
<div class="infor-card">
这里是截图内容。。。。。。
</div>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button class="lb-btn lb-btn-primary" @click="save()" :loading="btnLoad">
提交审批
</el-button>
</span>
</el-dialog>
</template>
<script>
import html2canvas from 'html2canvas'
import { convertBase64UrlToBlob } from 'lonbon-common/lib/file.js'
export default {
name: 'html2canvas',
data() {
return {
dialogVisible: false,
btnLoad: false,
pageLoad: false,
form: {},
rules: [],
};
},
methods: {
closed() {
this.btnLoad = false;
},
async open() {
this.dialogVisible = true;
},
async save() {
this.btnLoad = true;
await this.captureScreenshot('print-cost-form'); // 截图
let photoBlob = convertBase64UrlToBlob(this.screenshot, 'image/jpeg'); //Base64转Blob
// console.log(photoBlob, 'photoBlob');
data.approvalSnapshot = await this.$fileRequest({ //上传服务器
blob: photoBlob,
file: {
name: 'photo' + new Date().getTime() + '.jpg'
}
});
this.dialogVisible = false;
},
// 生成二维码
async captureScreenshot(id) { // 页面截图
await this.$nextTick();
const elementToCapture = document.getElementById(`${id}`); // 获取对应id元素
const canvas = await html2canvas(elementToCapture, {
allowTaint: true,
useCORS: true,
logging: false,
ignoreElements: (e) => { // 过滤节点
if(e.contains(elementToCapture) || elementToCapture.contains(e)){ // 当前循环的节点是目标节点的父级或子级
return false;
}
return true;
},
waitTime: 300
});
const screenshot =canvas.toDataURL('image/jpeg');
this.screenshot = screenshot;
},
},
};
</script>
<style lang="less" scoped></style>