问题背景
最近在工作中遇到了一个需求,在多页签应用中实现dom节点导出为图片的功能。需要将多个页签的dom节点捕获并转换为图片,然后上传到服务器,以便在报告中使用。
尝试过的方案
方案一:保存时捕获当前页面的图片信息
js
onSubmit() {
html2Canvas(this.$refs.container).then(canvas => {
const imageDataURL = canvas.toDataURL('image/png')
// 上传图片...
})
}
问题:这种方法只能捕获当前显示的图片信息,无法获取其他页签下的图片信息。
方案二:切换页签时捕获图片信息
js
watch: {
topTabIndex(newValue, oldValue) {
// 先捕获当前页面
html2Canvas(this.$refs.container).then(canvas => {
// 存储图片...
}).then(() => {
// 切换到新页签
this.currentData = this.formData.data[newValue].details[0]
})
}
}
问题:
-
页面切换卡顿严重
-
容易出现异步问题,导致捕获不完整
-
用户体验差
最终解决方案
采用了一种巧妙的方法:在DOM中预先渲染所有dom节点,将它们定位到视图之外,然后在提交时一次性捕获所有组件。
关键代码实现
- 在模板中渲染隐藏的Container组件
js
<div v-for="(item, index) in getData" :key="index" style="position: absolute; left: -9999px; top: -9999px;">
<Container class="Container" :current-data="item" style="margin-bottom: 20px;" />
</div>
2.提交时捕获所有Container
async
// 先捕获所有Container组件的图片
await this.captureAllContainers()
// 处理表单提交...
}
captureAllContainers() {
return new Promise((mainResolve) => {
setTimeout(() => {
const containers = document.querySelectorAll('.Container')
const containerPromises = []
Array.from(containers).forEach((container, index) => {
containerPromises.push(
new Promise((resolve) => {
html2Canvas(container, {
allowTaint: true,
useCORS: true,
scale: 2
}).then(canvas => {
const imageDataURL = canvas.toDataURL('image/png')
const file = this.base64ToFile(imageDataURL, `container-${index}.png`)
fileUpload([file]).then(res => {
// 更新数据...
resolve()
})
})
})
)
})
Promise.all(containerPromises).then(() => mainResolve())
}, 500)
})
}
解决方案优势
- 一次性渲染:所有Container组件在页面加载时就已经渲染,无需切换页签时重新渲染
-
用户体验佳:用户操作与图片捕获完全分离,不影响交互流畅度
-
可靠性高:通过Promise处理异步操作,确保所有图片都正确捕获
-
兼容性好:由于组件实际已渲染到DOM,html2Canvas可以正确捕获内容
通过这种隐藏渲染的方法,我们成功解决了多页签应用中捕获多个组件为图片的难题,既保证了功能的完整性,又确保了良好的用户体验。