背景
最近在工作中遇到了一个需求,点击按钮将Echart图复制到剪切板,然后按Ctrl(command)+V可以直接复制到聊天软件&文档编辑器中。本以为这是一个比较简单的需求,好像找了一圈资料,发现事情并不简单,故写下此文记录并分享。
常见方案
比较常见且有效的方案肯定是Clipboard API,不多说,直接上代码(手敲,如错误之处请指正):
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浏览器则无任何问题。
为了解决以上的兼容性问题,最终找到解决方案,不多说,上代码:
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);
})
}
完整代码
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复制图片至剪切板
注:原文也是作者所写。转载请注明出处,谢谢!
参考
JS复制图片到剪切板
如何在Safari中使用剪贴板API将图像写入剪贴板