Vue: 用html2canvas复制合成图片到剪贴板^o^避坑版

1,568 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天 点击查看活动详情

前言

需求描述

接到这么一个需求

  1. 点击“保存”,系统自动生成一张二维码图片(jpg),下载到本地
  2. 点击“复制”则将图片、链接复制到剪切板,并提示“已复制到剪切板”,然后可以粘贴到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相关的四个方法

  1. read方法返回一个Promise,可获取复制的内容
async function foo() {
  const items = await navigator.clipboard.read();
  const imageBlob = await items[0].getType("image/png");
  console.log("imageBlob==>", imageBlob);
}
  1. readText方法返回一个Promise,获取复制的文本内容
async function foo() {
  await navigator.clipboard.readText()
    .then(data => {
      console.log(data);
    });
}
  1. 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. :-(");
  }
);
  1. writeText方法写入文本内容
navigator.clipboard.writeText("hello!!!");

在这里我用到的是clipboard的第四个方法。

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天 点击查看活动详情