在 Web 开发中,有时我们需要将页面内容转换为图片,并且能够方便地复制到剪贴板。本文将介绍如何使用 html2canvas
实现这一功能,并展示如何在 Vue 项目中集成该功能。
背景需求
在现代 Web 应用中,常常需要将页面的某些部分截图,并进行保存或共享。例如:
- 用户界面反馈:用户可以截图并反馈界面问题。
- 数据展示:将数据图表、报告等内容生成图片,方便分享和保存。
- 内容存档:将动态生成的内容如发票、证书等保存为图片格式。
为了解决这些需求,我们可以使用 html2canvas
库,它能够将 HTML 元素及其样式渲染成 Canvas,并进一步转换为图片。
使用方法
我们以 Vue 项目为例,展示如何使用 html2canvas
生成图片,并一键复制到剪贴板。
首先,项目中安装 html2canvas
:
接下来,我们实现一个简单的 Vue 组件,包含需要截图的内容和一个按钮用于触发截图操作。
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
| <template> <div id="app"> <div ref="content" style="padding: 20px; background: lightgray;"> 这是需要截图并复制的内容。 </div> <button @click="handleClick">复制到剪贴板</button> </div> </template>
<script> import html2canvas from 'html2canvas';
export default { methods: { handleClick() { html2canvas(this.$refs.content.$el).then((canvas) => { canvas.toBlob( async (blob) => { console.log(blob); try { await navigator.clipboard.write([ new ClipboardItem({ [blob.type]: blob }), ]); console.log("copy success"); } catch (error) { console.log("copy fail"); } }, "image/png", 1 ); }); }, }, }; </script>
|
扩展功能
上面的方法只能获取固定元素的图片,接下来我们使用右键菜单的方式实现自定义选择导出图片,话不多说上代码:
APP.vue
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
| <template> <div class="home" @contextmenu.prevent="showContextMenu"> <MyComponent ref="MyComponent" /> <div class="box"> <p>本书覆盖 ES6 与上一个版本 ES5 的所有不同之处,对涉及的语法知识给予详细介绍,并给出大量简洁易懂的示例代码。</p> <p> 本书为中级难度,适合已经掌握 ES5 的读者,用来了解这门语言的最新发展;也可当作参考手册,查寻新增的语法点。如果你是 JavaScript 语言的初学者,建议先学完《JavaScript 语言教程》,再来看本书。 </p> <p> 全书已由电子工业出版社出版,2017年9月推出了第三版,书名为《ES6 标准入门》。纸版内容截止到出版时,网站内容一直在修订。 </p> </div> <!-- 右键菜单 --> <div v-if="contextMenuVisible" :style="{ top: `${contextMenuY}px`, left: `${contextMenuX}px` }" class="context-menu"> <ul> <li @click="copyToClipboard">复制为图片</li> </ul> </div> </div> </template>
<script> import MyComponent from "@/components/MyComponent.vue"; import html2canvas from "html2canvas";
export default { name: "Home", components: { MyComponent }, data() { return { contextMenuVisible: false, contextMenuX: 0, contextMenuY: 0, targetElement: null, }; }, methods: { copyElementToClipboard(element) { html2canvas(element).then((canvas) => { canvas.toBlob( async (blob) => { console.log(blob); try { await navigator.clipboard.write([ new ClipboardItem({ [blob.type]: blob }), ]); console.log("copy success"); } catch (error) { console.log("copy fail"); } }, "image/png", 1 ); }); }, showContextMenu(event) { this.contextMenuX = event.clientX; this.contextMenuY = event.clientY; this.targetElement = this.getClosestBodyChild(event.target); this.contextMenuVisible = true; document.addEventListener("click", this.hideContextMenu); }, hideContextMenu() { this.contextMenuVisible = false; document.removeEventListener("click", this.hideContextMenu); }, copyToClipboard() { this.hideContextMenu(); this.copyElementToClipboard(this.targetElement); }, getRootElement() { return document.querySelector(".home"); }, getClosestBodyChild(element) { while ( element.parentElement && element.parentElement !== this.getRootElement() ) { element = element.parentElement; } return element.parentElement === this.getRootElement() ? element : this.getRootElement(); }, }, }; </script>
|
MyComponent.vue
1 2 3 4 5
| <template> <div @contextmenu="$emit('contextmenu', $event)"> ... </div> </template>
|
- 右键点击时子组件内 contextmenu 事件触发,并将事件对象传递给父组件,父组件监听到 contextmenu 事件,并通过 prevent 修饰符阻止默认的右键菜单出现,自定义右键菜单在鼠标右键点击处显示,通过 getClosestBodyChild 方法获取到离某个元素节点最近的节点—这样做是为了避免点击某个元素内的子元素造成生成图片不全
- 点击复制选项时触发 copyToClipboard 事件,使用 html2canvas 库使 html 节点转换为 canvas,再通过 toBlob 方法转化成 blob 数据,再通过 navigator.clipboard 的 API 将 blob 数据复制到剪切板(这个可能要在浏览器手动开启权限)
- 这样图片就复制好了,在其他地方粘贴即可使用
可能遇到的问题
- 动态内容内容缺失:可以设置延时器或使用promise等待数据获取
1 2 3 4 5
| setTimeout(() => { html2canvas(element).then((canvas) => { }); }, 100);
|
未完待补充……