JavaScript如何实现复制图片功能?

最近开发中遇到一个需求,就是用户希望能通过直接点击按钮复制图片,然后就可以很方便的把图片发送到班群中,于是就有了复制图片的需求。

那么如何通过JavaScript来实现复制图片呢?

一、前置知识:如何实现复制?

既然要复制图片,那我们先看看前端是怎么实现复制功能的。

一般来说,实现复制有2种方案:

  1. document.execCommand()方法;
  2. Clipboard API

复制功能的安全限制:复制脚本需要放在监听事件回调里面进行执行,由用户触发(比如点击事件),如果直接执行脚本,浏览器可能会报错。

下面分别介绍下这2个方法:

1.1 document.execCommand()

使用方法:

先选中文本,然后调用 document.execCommand('copy'),即可将选中的文本复制到剪贴板。

js 复制代码
const input = document.getElementById("input");
input.select(); // 注意 input 框不能添加 disabled 属性,否则会影响 select() 方法
document.execCommand("copy");

同样的它还可以实现剪切粘贴功能。

  1. document.execCommand('cut'):剪切选中的文本到剪贴板。
  2. document.execCommand('paste'):从剪贴板粘贴文本到光标位置。

剪切功能的使用和复制一样,所以这里就不做演示了。我这边再演示一下粘贴功能。

粘贴功能有以下使用限制:

  1. 确保元素是可编辑的,比如inputtextareacontenteditable属性为true的元素。
  2. 确保元素是焦点元素,可以使用focus()方法将焦点设置到元素上。
js 复制代码
const target = document.getElementById('pasteTargetInput');
target.focus(); // 确保目标元素获得焦点
document.execCommand('paste'); // 尝试粘贴

但是现代浏览器比如(Chrome、Firefox、Edge)已经限制或废弃了 execCommand("paste") 的直接使用,所以不推荐用这个api了

最后总结一下,document.execCommand()是实现复制的一个传统方法,它的优缺点如下:

  1. 优点:低版本浏览器兼容性好,
  2. 缺点:
  • 已被web 标准弃用,不推荐使用。
  • 只能复制文本,不能复制图片或者二进制数据。
  • 只能讲选中的内容写入到剪贴板,不能直接写入自定义内容。
  • 它是同步操作,如果复制内容过多,会引起页面卡顿。

1.2 Clipboard API

在浏览器的BOM对象中,有一个APIclipboard,它提供了系统剪贴板的读写访问能力 ,可以实现剪切、复制和粘贴功能,我们可以通过window上的navigator对象直接访问到一个clipboard对象。

js 复制代码
console.log(navigator.clipboard);

clipboard提供了四个方法:

  1. read():从剪切板读取数据,它会返回一个Promise,并将数据包装成一个ClipboardItem对象回传,
  2. readText():从剪切板中读取文本,它会返回一个Promise对象,并将剪切板数据作为String回传,
  3. write():写入数据(ClipboardItem对象)到剪切板,它会返回一个PromisePromise成功意味着写入完成,
  4. writeText():写入文本到剪切板,它会返回一个PromisePromise成功意味着写入完成。

我们可以通过上面描述的write()writeText()方法实现复制功能,writeText()只能写入文本,而write()方法则可以写入任意数据,所以我们需要使用write()方法实现复制图片的功能。

二、实现复制图片

一般图片是通过两种形式加载:

  1. 远程URL,最常见的是cdn 地址,比如https://cdn.xxx.cn/example.png
  2. base64 URL,一些体积较小的图片通常会打包成base64字符串,以减少网站资源请求数量,比如data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...

不管是什么图片形式的图片,我们最终目的都是需要将它转化为blob对象,然后通过navigator.clipboard.write()方法写入到剪贴板。

比如这样一张图片:

html 复制代码
<img src="https://xxx" alt="加载失败" id="img"/>

我们可以通过 canvas 将图片复制到剪贴板,具体代码如下:

js 复制代码
function copyImage(img) {
  const canvas = document.createElement('canvas')
  canvas.width = img.width
  canvas.height = img.height
  const ctx = canvas.getContext('2d')
  ctx.drawImage(img, 0, 0, img.width, img.height)
  canvas.toBlob((blob) => {
    // 目前图片只支持 png 类型
    const clipboardItem = new ClipboardItem({ 'image/png': blob })
    navigator.clipboard.write([clipboardItem])
  })
}
img.addEventListener("click", () => {
  copyImage(img);
});

这里如果出现Uncaught SecurityError: Failed to execute 'toBlob' on 'HTMLCanvasElement'报错,可能是你使用的图片跨域了,首先确保图片资源服务器允许跨域使用,然后需要在img标签上增加crossOrigin属性。

html 复制代码
<img src="xxx" crossOrigin/>

当然实际业务中可能提供的只是一个图片点击后的跳转链接,需要先把链接转成一张二维码图片,再进行复制。

这里使用第三方库qrcode,把链接转化成二维码。

js 复制代码
import QRcode from 'qrcode';

async function copyImage(url) {
  // 转成 base64 的 url
   const url = await QRcode.toDataURL(link, {
      errorCorrectionLevel: 'H', // 纠错级别最高
      width: 128,
      margin: 2,
      scale: 1,
    });
    const blob = await (await fetch(url)).blob();
    const clipboardItem = new ClipboardItem({ 'image/png': blob })
    navigator.clipboard.write([clipboardItem])
}

我们这里通过QRcode.toDataURL()方法,拿到二维码的base64图片url,然后通过原生的fetch方法,把二维码图片转化成blob对象,最后以ClipboardItem形式写入到clipboard中。

除了fetch以外,还可以通过如下方式将base64图片转化为blob对象,具体代码如下:

js 复制代码
function dataURIToBlob(dataURI) {
    const base64Index = dataURI.indexOf(';base64,') + 8;
    const base64 = dataURI.substring(base64Index);
    const byteCharacters = atob(base64);
    const byteArrays = [];

    for (let i = 0; i < byteCharacters.length; i++) {
      byteArrays.push(byteCharacters.charCodeAt(i));
    }

    const byteArray = new Uint8Array(byteArrays);
    const blob = new Blob([byteArray], { type: 'image/png' });
    return blob;
}

三、clipboard兼容性问题

在一些浏览器上可能不支持Clipboard API,所以需要通过navigator.clipboard的值是否为undefined判断此浏览器是否支持复制功能,如果不支持Clipboard API的话,就无法复制图片了,这时候可以使用document.execCommand('copy')来复制文本,或者直接给用户提示该浏览器不支持复制图片。

js 复制代码
 // 将dataBase64复制到剪切板
function copyToClipboard(text) {
  let a = document.createElement('input');
  a.value = text;
  document.body.appendChild(a);
  a.select();
  document.execCommand('copy');
  a.remove();
}

function copyImage() {
  if (navigator.clipboard) {
     //...
      const clipboardItem = new ClipboardItem({ 'image/png': imgBlob });
      navigator.clipboard.write([clipboardItem]);
      console.log('复制成功');
  } else {
    copyToClipboard(img.src);
  }
}

四、其它功能

4.1 预览图片

预览图片主要依赖于URL.createObjectURL这个原生API,具体代码如下:

js 复制代码
function previewImage(img) {
  const url = URL.createObjectURL(blob);
  const img = document.createElement('img');
  img.src = url;
  document.body.appendChild(img);
}

4.2 下载图片

下载图片主要依赖于a标签的download属性,具体代码如下:

js 复制代码
function downloadImage(img) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = true;
  a.click();
  URL.revokeObjectURL(url);
  console.log('下载图片成功');
}

五、小结

本文主要介绍了前端如何实现复制图片的功能,核心是通过Clipboard API来实现的,如遇到浏览器兼容问题,可以考虑回退使用document.execCommand()进行低版本的兼容,另外还介绍了预览图片下载图片的实现思路,希望能帮助到大家!

相关推荐
To_OC8 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
To_OC9 小时前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
vipbic9 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy9 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志9 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble11 小时前
c端系统,其实更像一个信息展示平台
前端
你怎么知道我是队长11 小时前
JavaScript的变量和数据类型介绍
开发语言·javascript·ecmascript
李姆斯11 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜12 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司12 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能