常用函数之js复制图片至剪切板

背景

最近在工作中遇到了一个需求,点击按钮将Echart图复制到剪切板,然后按Ctrl(command)+V可以直接复制到聊天软件&文档编辑器中。本以为这是一个比较简单的需求,好像找了一圈资料,发现事情并不简单,故写下此文记录并分享。

常见方案

比较常见且有效的方案肯定是Clipboard API,不多说,直接上代码(手敲,如错误之处请指正):

js 复制代码
const copyToBoard1 = (url: string) => {
    const img = new Image();
    img.src = url;
    img.setAttribute('crossOrigin', 'Anonymous');
    img.onload = () => {
        const width = img.width;
        const height = img.height;
        const newCanvas = document.createElement('canvas');
        const ctx = newCanvas.getContext('2d');
        if(ctx) {
            newCanvas.drawImage(img, width, height);
            newCanvas.toBlob(blob => {
                navigator.clipboard.write([         
                    new ClipboardItem({           
                        'image/png': blob,         
                    }),       
                ]).then(() => {
                    console.log('success');
                }).catch((e) => {
                    console.error(e);
                })
            }, 'image/png')
        }
    }
}

上述函数可以直接用在chrome中,但是存在一些问题:

  • 1、clipboard API 只支持png格式;
  • 2、存在兼容性问题,兼容性如下图:

从兼容性上来看,clipboard API几乎支持除IE之外的所有浏览器。但是,从我本次经历来看,在Safari浏览器、QQ浏览器上也存在兼容性问题(环境:MAC电脑,其余浏览器未曾测试),Chrome浏览器则无任何问题。

为了解决以上的兼容性问题,最终找到解决方案,不多说,上代码:

typescript 复制代码
const copyToBoard2 = (url: string) => {
    const makeImage = async () => {
        const res = await fetch(url);
        return res.blob();
    }
    navigator.clipboard.write([         
        new ClipboardItem({           
            'image/png': makeImage(),         
        }),       
    ]).then(() => {
        console.log('success');
    }).catch((e) => {
        console.error(e);
    })
}

完整代码

typescript 复制代码
import UAParser from 'ua-parser-js';

const hasCompatibilityNameList = [
    'safari',
    'qqbrowser'
]
const getHasCompatibilityName = () => {
    const parser = new UAParser();
    const res = parser.getResult();
    const name = res?.browser.name.toLowerCase();
    
    return name ? hasCompatibilityNameList.includes(name) : false;
}
const makeImageMethod1 = async (url:string) => {
    try{
        const res = await fetch(url);
        return res.blob();
    } catch {
        return ''
    }
}

const makeImageMethod2 = (url: string) => {  
  return new Promise((resovle) => {  
    const img = new Image();  
    img.src = url;  
    img.setAttribute('crossOrigin', 'anonymous');  
    img.onload = () => {  
      const newCanvas = document.createElement('canvas');  
      newCanvas.width = img.width;  
      newCanvas.height = img.height;  
      const ctx = newCanvas.getContext('2d');  
      if (ctx) {  
        ctx.drawImage(img, 0, 0);  
        newCanvas.toBlob((blob) => {  
          navigator.clipboard  
            .write([  
              new ClipboardItem({  
                'image/png': blob || '',  
              }),  
            ])  
            .then(  
              () => {  
                resovle(true)  
              },  
              () => {  
                resovle(false)  
              }  
            );  
        }, 'image/png');  
      }  
    };  
    img.onerror = () => {  
      resovle(false)  
    };  
  })  
}

export const copyToBoard = async (url: string) => {  
  const isSafariOrQQ = getHasCompatibilityName();  
  let flag = true;
  if (isSafariOrQQ) {  
    navigator.clipboard.write([  
      new ClipboardItem({ 'image/png': makeImagePromise(url) }),  
    ]).then(() => {  
      console.log('复制成功')  
    }).catch(async () => {  
      const res = await makeImageMethod2(url);  
      if (!res) {  
        flag = false  
      }  
    })  
  } else {  
    const res = await makeImageMethod2(url);  
    if (!res) {  
      navigator.clipboard.write([  
        new ClipboardItem({ 'image/png': makeImagePromise(url) }),  
      ]).then(() => {  
        console.log('复制成功')  
      }).catch(() => {  
        flag = false  
      })  
    }  
  }  
  return flag;  
}

最终,选择了2种方案结合的形式:

  • 1、判断当前浏览器环境是否是Safari、QQ

    • 1、失败、异常,则调用方法2;
  • 2、其余环境

    • 1、失败、异常,则调用方法1;
  • 3、方法1、2都失败,则返回false;

抛出false则认为2种函数在当前浏览器环境均不可成功复制。

这里的逻辑就是考虑环境判断不完整,将2个函数都调用一遍,兜底作用。

实测上述方案可以正常在Safari、QQ、Chrome浏览器中正常复制图片到剪切板。

参考

JS复制图片到剪切板

如何在Safari中使用剪贴板API将图像写入剪贴板

相关推荐
清灵xmf20 分钟前
提前解锁 Vue 3.5 的新特性
前端·javascript·vue.js·vue3.5
Jiaberrr26 分钟前
教你如何在微信小程序中轻松实现人脸识别功能
javascript·微信小程序·小程序·人脸识别·百度ai
白云~️33 分钟前
监听html元素是否被删除,删除之后重新生成被删除的元素
前端·javascript·html
2401_8644769334 分钟前
无线领夹麦克风哪个降噪好?一文搞懂麦克风什么牌子的音质效果好
javascript·git·sql·github·mssql
尸僵打怪兽2 小时前
后台数据管理系统 - 项目架构设计-Vue3+axios+Element-plus(0920)
前端·javascript·vue.js·elementui·axios·博客·后台管理系统
ggome2 小时前
Uniapp低版本的安卓不能用解决办法
前端·javascript·uni-app
Ylucius2 小时前
JavaScript 与 Java 的继承有何区别?-----原型继承,单继承有何联系?
java·开发语言·前端·javascript·后端·学习
前端初见2 小时前
双token无感刷新
前端·javascript
bin91532 小时前
前端JavaScript导出excel,并用excel分析数据,使用SheetJS导出excel
前端·javascript·excel
.生产的驴3 小时前
SpringBoot 消息队列RabbitMQ 消息确认机制确保消息发送成功和失败 生产者确认
java·javascript·spring boot·后端·rabbitmq·负载均衡·java-rabbitmq