Open
Description
一、背景
项目里有个上传图片的功能,但用户经常上传截屏图片,此时用户的操作流程是截屏 -> 保存图片-> 选择图片并上传
。为了用户体验也为了实现产品吹的牛皮,需要将3步改成2步,即截屏 ->上传
。
技术调研
截屏操作获取的图片存储在系统剪切板,所以得利用JS读取剪切板内容。研究下Clipboard API的使用。
测试环境
- Chrome V87
- FF V95
二、Navigator.clipboard
利用全局属性Navigator.clipboard
可以利用JS操作系统剪切板。
使用限制
JS方式读写系统剪切板涉及用户隐私和数据安全问题,这个API使用场景有使用限制:
-
安全上下文(HTTPS)
必须是HTTPS
或者localhost
域名,否则不会暴露Navigator.clipboard
属性:
-
文档如果以
iframe
方式被嵌入其他跨域文档中,则也不能执行
-
Chrome V87
详情见弃用跨域 iframe 中的权限
读数据Clipboard.read()
navigator.clipboard.read().then((data) => {
for (let i = 0; i < data.length; i++) {
if (!data[i].types.includes("image/png")) {
alert("Clipboard contains non-image data. Unable to access it.");
} else {
data[i].getType("image/png").then((blob) => {
imgElem.src = URL.createObjectURL(blob);
});
}
}
});
三、粘贴(paste)事件
监听用户触发的粘贴操作。此时粘贴操作是用户主动触发的,所以排除了一些安全问题。
使用限制对比
使用限制 | Navigator.clipboard API |
粘贴(paste)事件 |
---|---|---|
安全上下文(HTTPS) | Y | N |
用户授权 | Y | N |
文档要处于Focused状态 | Y | Y |
弃用跨域 iframe 中的权限 | Y | N |
ClipboardEvent.clipboardData
属性
利用ClipboardEvent.clipboardData
属性可以获取系统剪切板的数据。
总结下ClipboardEvent.clipboardData
对象的几个比较重要的属性和方法:
属性/方法名称 | 描述 |
---|---|
types属性 | 剪切板数据的格式列表(一份数据可以存在多个展示格式),比如text/plain , text/html , Files 等 |
items属性 | 剪切板里的数据列表,和types 属性的值一一对应 |
getData方法 | 获取剪切板数据,但不能获取文件内容(得使用files 属性) |
files属性 | 文件内容,比如截图操作 |
实战
获取文本内容
window.addEventListener('paste', function(e) {
const clipboardData = e.clipboardData || window.clipboardData;
// 或者clipboardData.getData('text');
const data = clipboardData.getData('text/plain');
console.log(data)
})
获取富文本内容
即copy带有样式的文本。
window.addEventListener('paste', function(e) {
const clipboardData = e.clipboardData || window.clipboardData;
const data = clipboardData.getData('text/html');
console.log(data)
})
获取并展示粘贴板数据
根据types
属性的值读取剪切板数据。
window.addEventListener('paste', function(e) {
const clipboardData = e.clipboardData || window.clipboardData;
// 数据的格式列表(一份数据可以存在多个展示格式)
const { types } = clipboardData;
types.forEach(type => {
console.log(`type=${type}`)
const data = clipboardData.getData(type);
// 不携带样式的文本(包含回车,换行等不可见字符)
if('text/plain' === type) {
document.getElementById('js-text').innerHTML = data;
return;
}
// 富文本格式
if('text/html' === type) {
document.getElementById('js-html').innerHTML = data;
return;
}
})
// 文件
if(clipboardData.files.length) {
clipboardData.files.forEach(file => {
if(!/^image/.test(file.type)) {
return;
}
const reader = new FileReader();
reader.addEventListener("load", function () {
document.getElementById('js-img-preview').src = reader.result;
}, false);
reader.readAsDataURL(file);
})
}
})
兼容性
目前测试Chrome(V87),FF(V95)可以正常使用。详情见兼容性