当前位置: 首页 > 科技观察

想要复制图像?了解剪贴板API

时间:2023-03-16 17:03:43 科技观察

我写了这个29.7K剪贴板JS库,有东西!这篇文章之后,收到了朋友的两个问题:1.这个库除了可以复制文字外,还能复制图片吗?2、clipboard.js库依赖的document.execCommand接口已经废弃。以后怎么办?(图片来源:https://developer.mozilla.org/zh-CN/docs/Web/API/Document/execCommand)接下来,本文将重点讨论以上两个问题,但在看第一个问题之前,我们先简单介绍一下介绍剪贴板📋。剪贴板(英文:clipboard),有时也称为剪贴板、剪贴簿、剪贴簿。它是一种软件功能,通常由操作系统提供,用于使用复制和粘贴操作在文档或应用程序之间短期存储和传输数据。它是图形用户界面(GUI)环境中最常用的功能之一,通常作为匿名的临时数据缓冲区实现,环境中的大多数或所有程序都可以使用编程接口访问该缓冲区。——维基百科从上面的描述我们可以知道,剪贴板搭建了一座桥梁,使得各种应用程序之间的信息传递和共享成为可能。不过美中不足的是,剪贴板只能保留一份数据,每当导入新数据时,旧数据就会被覆盖。了解了剪贴板的概念和作用之后,我们来看第一个问题:clipboard.js库除了可以复制文字外,还可以复制图片吗?1、clipboard.js可以复制图片吗?clipboard.js是一个用于将文本复制到剪贴板的JS库。没有Flash,没有框架,启用gzip压缩后只有3kb。(图片来源:https://clipboardjs.com/#example-text)当您看到描述“一种将文本复制到剪贴板的现代方法”时,您是否已经知道答案了?那么实际情况如何呢?让我们亲手验证一下。这个29.7K的剪贴板JS库里有东西!阿宝哥在这篇文章中介绍,在实例化ClipboardJS对象时,可以通过options对象的target属性来设置复制目标://https://github。com/zenorocha/clipboard.js/blob/master/demo/function-target.htmlletclipboard=newClipboardJS('.btn',{target:function(){returndocument.querySelector('div');}});使用剪贴板。js特性,我们可以定义如下HTML结构:

大家好,我是阿宝哥

复制然后在实例化ClipboardJS对象的时候设置复制的目标是#container元素:constclipboard=newClipboardJS(".btn",{target:function(){returndocument.querySelector("#container");}});之后,我们点击页面上的复制按钮,对应的效果如下图所示:观察上图,页面中的图片和文字都被复制了。对于正文,大家应该都很清楚了。至于图像,究竟复制了什么?我们如何获取复制的内容?为了解决这个问题,我们可以使用HTMLElement对象上的onpaste属性或者监听元素上的paste事件。这里我们通过设置document对象的onpaste属性打印粘贴事件对应的事件对象:document.onpaste=function(e){console.dir(e);}页面,控制台会打印出如下内容:从上图可以看出,ClipboardEvent对象包含一个clipboardData属性,该属性包含了与剪贴板关联的数据。详细分析clipboardData属性后,我们发现复制的图片和普通文本被封装为DataTransferItem对象。为了更方便的分析DataTransferItem对象,阿宝哥重新更新了document对象的onpaste属性:在上图中,我们可以清楚的看到DataTransferItem对象包含了kind和type属性来表示数据项的类型(string或文件)和数据对应的MIME类型。利用DataTransferItem对象提供的getAsString方法,我们可以获取到该对象中存储的数据:相信看完上面的输出结果,小伙伴们对第一个问题的答案就心知肚明了。那么如果你想复制图像,你应该怎么做呢?其实这个问题的答案和小伙伴提出的第二个问题的答案是一样的。我们可以使用ClipboardAPI来实现复制图片的问题,解决document.execCommandAPIDeprecated的问题。接下来我们的目标是实现复制图片的功能,因为需要用到剪贴板API,所以阿宝哥先介绍一下API。2.ClipboardAPI介绍Clipboard接口实现了ClipboardAPI。如果用户授予相应的权限,它可以提供对系统剪贴板的读写访问。在Web应用程序中,剪贴板API可用于实现剪切、复制和粘贴功能。该API用于替代document.execCommandAPI实现的剪贴板操作。在实际项目中,我们不需要手动创建Clipboard对象,而是通过navigator.clipboard获取Clipboard对象:获取Clipboard对象后,我们可以使用该对象提供的API来访问剪贴板,例如:导航器.剪贴板。readText().then(clipText=>document.querySelector(".editor").innerText=clipText);上面的代码将HTML中包含.editor类的第一个元素的内容替换为剪贴板的内容。如果剪贴板为空,或不包含任何文本,则元素的内容将被清空。这是因为当剪贴板为空或不包含文本时,readText方法返回一个空字符串。在继续介绍剪贴板API之前,我们先了解一下NavigatorAPI:剪贴板兼容性:(来源:https://caniuse.com/mdn-api_navigator_clipboard)异步剪贴板API是一个比较新的API,浏览器还在完善中逐步实施。由于潜在的安全问题和技术复杂性,大多数浏览器都在逐渐集成这个API。对于浏览器扩展,您可以请求clipboardRead和clipboardWrite权限以使用clipboard.readText()和clipboard.writeText()。好了,接下来阿宝哥就来演示如何使用剪贴板对象提供的API来操作剪贴板。以下示例的运行环境为Chrome87.0.4280.88。3、向剪贴板写入数据3.1writeText()writeText方法可以将指定的字符串写入系统的剪贴板。调用该方法后,会返回一个Promise对象:.log("页面地址已经复制到剪贴板");}catch(err){console.error("页面地址复制失败:",err);}}对于上面的代码,当用户点击复制当前页面地址按钮,当前页面地址将被复制到剪贴板。3.2write()write方法除了支持文本数据,还支持将图片数据写入剪贴板。调用此方法后,将返回一个Promise对象。复制当前页面地址在上面的代码中,我们首先通过BlobAPI创建了一个Blob对象,然后使用Blob对象构造了一个ClipboardItem对象,最后将数据写入到通过write方法写入剪贴板。介绍完如何向剪贴板写入数据,我们再介绍一下如何从剪贴板读取数据。4.从剪贴板中读取数据4.1readText()readText方法用于读取剪贴板中的文本内容。调用该方法后,会返回一个Promise对象:);console.log("剪贴板内容已读取:",text);}catch(err){console.error("读取剪贴板内容失败:",err);}}script>对于上面的代码,当用户点击按钮读取剪贴板中的文本时,如果当前剪贴板中包含文本内容,则会读取剪贴板中的文本内容。4.2read()read方法除了读取文本数据外,还支持读取剪贴板中的图片数据。调用该方法后,会返回一个Promise对象:constclipboardItemofclipboardItems){for(consttypeofclipboardItem.types){constblob=awaitclipboardItem.getType(type);console.log("剪贴板的内容已被读取:",awaitblob.text());}}}catch(err){console.error("Failedtoreadthecontentsoftheclipboard:",err);}}对于上面的代码,当用户点击读取剪贴板内容的按钮时,会开始读取剪贴板的内容。阿宝哥已经介绍了剪贴板对象涉及的4个API。最后,我们来看看如何实现复制图片的功能。5、实现复制图片的功能最后一个例子,阿宝哥就跟着大家一步步实现复制图片的核心功能。除了复制图片,它还将支持复制文本。在看具体代码之前,我们先来看看实际效果:在上图对应的网页中,我们先点击复制按钮,图片和文字都会被选中。之后,当我们点击粘贴按钮时,控制台会输出从剪贴板读取的实际内容。在分析具体实现方法之前,我们先看一下对应的页面结构:

大家好,我是阿宝哥

复制粘贴上面的页面结构非常简单。下面我们一步步分析上述函数的实现过程。5.1请求剪贴板写入权限默认情况下,剪贴板写入权限会自动授予当前活动页面。为了安全起见,这里我们还是主动请求用户写入剪贴板:asyncfunctionaskWritePermission(){try{const{state}=awaitnavigator.permissions.query({name:"clipboard-write",});returnstate==="granted";}catch(error){returnfalse;}}5.2将图片和普通文本数据写入剪贴板将图片数据写入剪贴板需要使用navigator.clipboard提供的write方法目的。如果我们要写入图片数据,就需要获取图片对应的Blob对象。这里我们可以通过fetchAPI从网络获取图片对应的response对象,并转化为Blob对象。具体实现方法如下:asyncfunctioncreateImageBlob(url){constresponse=awaitfetch(url);returnawaitresponse.blob();}对于普通文本,只需要使用前面介绍的BlobAPI,即可将普通文本转换为Blob对象:functioncreateTextBlob(text){returnnewBlob([text],{type:"text/plain"});}创建完图片和普通文本对应的Blob对象后,我们就可以使用它们来创建ClipboardItem对象,然后调用write方法将这些数据写入剪贴板,对应的代码如下:asyncfunctionwriteDataToClipboard(){if(askWritePermission()){if(navigator.clipboard&&navigator.clipboard.write){consttextBlob=createTextBlob("大家好,我是阿宝哥");constimageBlob=awaitcreateImageBlob("http://cdn.semlinker.com/abao.png");try{constitem=newClipboardItem({[textBlob.type]:textBlob,[imageBlob.type]:imageBlob,});选择(document.querySelector("#container"));awaitnavigator.clipboard.write([item]);console.log("Textandimagecopysuccessful");}catch(error){console.error("Textandimagecopyfailed",error);}}}}在上面的代码中,使用了一个select方法,该方法用于实现选择效果,对应代码如下:();selection.addRange(range);}通过writeDataToClipboard方法,我们已经将图像和普通文本数据写入剪贴板。现在让我们使用navigator.clipboard对象提供的read方法来读取写入的数据。如果需要读取剪贴板数据,需要向用户请求剪贴板读取权限。5.3请求剪贴板读取权限这里我们定义一个asyncfunctionaskReadPermission函数向用户请求剪贴板读取权限:asyncfunctionaskReadPermission(){try{const{state}=awaitnavigator.permissions.query({name:"clipboard-read",});returnstate==="granted";}catch(error){returnfalse;}}调用askReadPermission方法时,会请求当前用户读取剪贴板。对应的效果如下图所示:5.4读取剪贴板写入数据创建askReadPermission函数后,我们可以使用前面介绍的navigator.clipboard.read方法读取剪贴板数据:asyncfunctionreadDataFromClipboard(){if(askReadPermission()){if(navigator.clipboard&&navigator.clipboard.read){try{constclipboardItems=awaitnavigator.clipboard.read();for(constclipboardItemofclipboardItems){console.dir(clipboardItem);for(consttypeofclipboardItem.types){constblob=awaitclipboardItem。getType.log(类型)缺点;("剪贴板内容已读取:",awaitblob.text());}}}catch(err){console.error("读取剪贴板内容失败:",err);}}}}其实除了点击粘贴按钮,我们还可以通过监听粘贴事件读取剪贴板中的数据。需要注意的是,如果当前浏览器不支持异步剪贴板API,我们可以使用clipboardData.getData方法读取剪贴板中的文本数据:document.addEventListener('paste',async(e)=>{e.preventDefault();lettext;if(navigator.clipboard){text=awaitnavigator.clipboard.readText();}else{text=e.clipboardData.getData('text/plain');}console.log('获取的文本数据:',文本);});对于图像数据,可以通过以下方式读取:constIMAGE_MIME_REGEX=/^image\/(p?jpeg|gif|png)$/i;document.addEventListener("paste",async(e)=>{e.preventDefault();if(navigator.clipboard){letclipboardItems=awaitnavigator.clipboard.read();for(constclipboardItemofclipboardItems){for(consttypeofclipboardItem.types){if(IMAGE_MIME_REGEX.test(type)){constblob=awaitclipboardItem.getType(type);loadImage(blob);返回;}}}}else{constitems=e.clipboardData.items;for(leti=0;i