开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天 点击查看活动详情
前言
需求描述
接到这么一个需求
- 点击“保存”,系统自动生成一张二维码图片(jpg),下载到本地
- 点击“复制”则将图片、链接复制到剪切板,并提示“已复制到剪切板”,然后可以粘贴到qq、微信等平台聊天窗口
当时心想我只弄过复制文本的功能,可没试过复制图片到剪贴板,一时没思路,后面经过一路的折腾和探索,才发现,这个原理,其实跟生成分享图片差不多,将图文合成一个新的图片。然后再将图片传送到剪贴板即可。
在探索的过程中,基本查到的都是js相关较为复杂的实现方法,就是各种数据值的转换,然后组装,我是个比较追求简化的实现风格的人,降低实现成本为主
那么要实现这个图文复制,接下来就得说说html2canvas
,还有一个JS提供的对象navigator.clipboard
html2canvas用法及避坑
什么是 html2canvs?
html2canvas 的作用就是允许让我们直接在用户浏览器上拍摄网页或其部分的“截图”。
它的屏幕截图是基于 DOM 的,因此可能不会 100% 精确到真实的表示,因为它不会生成实际的屏幕截图,而是基于页面上可用的信息构建屏幕截图。
html2canvas 可以用来做什么
从上的面的介绍可以知道, html2canvas 的作用就是根据 DOM 生成对应的图片,所以一个比较常见的应用场景就是我们可以使用它在 H5 端生成分享图
如图:这种就是利用了html2canvas的特性实现的
当然并不是说它只能用于H5端,而是依据需求,用到类似的功能我们都可以通过这种方式去实现的呢。
用法
效果
代码
先贴代码进行讲解:
<span id="code-img" class="code">
<span class="code-title">金山软件</span>
<span class="code-depart">加入部门:产研规划部</span>
<img src="@/assets/img/twoCode.jpg" class="code-img" />
</span>
<span class="btns">
<t-button theme="default" variant="outline">保存</t-button>
<t-button theme="primary" @click="onCopyImage">复制</t-button>
</span>
const copyImg = () => {
html2canvas(document.getElementById('code-img')).then(async (canvas) => {
imgUrl = canvas.toDataURL();
console.log(imgUrl);
const data = await fetch(imgUrl);
const blob = await data.blob();
await navigator.clipboard.write([
// eslint-disable-next-line no-undef
new ClipboardItem({
[blob.type]: blob,
}),
]);
});
};
就是通过上面的方法,实现了剪切图片功能,但是这里有个问题,我复制后,粘贴到微信聊天窗口后发现,是个白色的图片,呵呵🐶,后面才发现,原来是图片跨域了
这个时候我们去看它的请求,可以看到它本身就没有做跨域的相关配置。 对于允许跨域的图片我们可以在 Headers 里面看到
Access-Control-Allow-Origin:*
对于这个问题,最简单的解决方案就是直接在所在图片的 img 标签里面加上 crossOrigin = "anonymous",即:
<img crossorigin="anonymous" src="xxx" >
在某些情况下如果我发现加上 crossOrigin = "anonymous" 之后,图片显示不出来了,此时给图片的 url 中拼上一个随机字符串即可。
<img crossorigin="anonymous" :src="`xxx?_=${Date.now()}`" >
当然,想要永久的解决这个问题需要后端同学配合在图片服务器上设置 图片服务器配置 Access-Control-Allow-Origin: *。
避坑
所以我将前面那段代码进行了改写如下:
html2canvas(document.getElementById('code-img'), {
useCORS: true, // 【重要】开启跨域配置
allowTaint: true, // 允许跨域图片
}).then()...
能完整地复制到了我想要的效果
关于navigator.clipboard
clipboard api兼容情况,注意高版本浏览器支持,IE是完全不支持的
在这里介绍一下clipboard相关的四个方法
- read方法返回一个Promise,可获取复制的内容
async function foo() {
const items = await navigator.clipboard.read();
const imageBlob = await items[0].getType("image/png");
console.log("imageBlob==>", imageBlob);
}
- readText方法返回一个Promise,获取复制的文本内容
async function foo() {
await navigator.clipboard.readText()
.then(data => {
console.log(data);
});
}
- write方法,写入剪切板内容,如图片或其它文件等
const data = [
new ClipboardItem({
"text/plain": new Blob(["hello clipboard"], { type: "text/plain" }),
}),
];
navigator.clipboard.write(data).then(
() => {
console.log("Copied to clipboard successfully!");
},
() => {
console.error("Unable to write to clipboard. :-(");
}
);
- writeText方法写入文本内容
navigator.clipboard.writeText("hello!!!");
在这里我用到的是clipboard的第四个方法。
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天 点击查看活动详情