首页/文章/javascript

又get到了,JS复制图片到剪切板

2023-12-26
21648 分钟
...

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11015 鑫空间-鑫生活
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。

一、总要有原因的

为什么会突然想到了解如何使用JS复制图片到剪切板呢?

因为上周弄了个PNG/JP在线压缩的工具,一开始的时候,图片获取的途径只能是下载到本地。

但很多时候,我是希望这种压缩好的图片可以直接上传,不需要再从本地转一圈。

此时,如果有一个复制功能就好了。

直接点击一个按钮,压缩好的图片复制到剪切板,然后再去上传页面进行上传,多方便,多轻松。

所以,就去了解了下,还是学到了不少东西的。

这里和大家分享下。

首先,先从简单的开始。

二、原生的图片复制API

浏览器提供了一个名为 ClipboardItem 的实例对象,可以构造构造剪切板复制对象。

例如一段文本的复制(代码取自MDN文档):

function setClipboard(text) {
  const type = "text/plain";
  const blob = new Blob(\[text\], { type });
  const data = \[new ClipboardItem({ \[type\]: blob })\];

  navigator.clipboard.write(data).then(() => {
    
  }, () => {
    
  });
}

图片也不在话下。

我专门写了个复制图片的方法,参数就是页面中的IMG元素对象。

const doCopyImg2Clipboard = async (image, success = () => {}, failure = () => {}) =\> {
    if (!image || !image.src) {
        return;    
    }
    
    
    const src = image.src;
    
    const response = await fetch(src);
    
    
    const blob = response.blob();
    
    
    const data = \[new ClipboardItem({
        \[blob.type || 'image/' \+ src.split('.').slice(-1)\[0\].replace('jpg', 'jpeg')\]: blob    
    })\];
    
    navigator.clipboard.write(data).then(success, failure);
}

如果你已经有了图片的数据(例如本地上传、拖拽或粘贴的图片),则fetch步骤可以略过。

所以,当页面中有如下所示的HTML代码(一张图,一个按钮):

<button id="button" type="primary">复制图片</button>
<img id="image" src="./mybook.jpg">

就可以使用一个简单的点击事件,触发图片的复制了。

例如:


button.addEventListener('click', () => {
    doCopyImg2Clipboard(image, function () {
        new LightTip('复制成功', 'success');
    }, function (err) {
        new LightTip('复制失败:' \+ err, 'error');
    });
});

然而,上面的代码得到的结果却是失败!

从上面的错误提示可以看出,浏览器是不支持 jpg 格式的图片信息写入剪切板的。

试试PNG呢?

如果我们把图片的JPG格式,转换为PNG格式,再试一试,比方说:

<button id="button" type="primary">复制图片</button>
<img id="image" src="./mybook.png">

则就可以成功复制!

眼见为实,您可以狠狠地点击这里:复制JPG和PNG图片到剪切板是否成功demo

比方说,我们点击demo右侧的那个按钮,此时,提示的就会是复制成功,如下截图示意。

此时,PNG图片信息就已经在剪切板中了。

我们可以找个富文本输入框,或者在线文档编辑器等进行测试。

例如demo页面提供的输入框,我们执行下Ctrl+V,则可以看到图片出现了,如下图所示。

OK,好,下面问题来了,页面中有些图片他就是JPG格式的,我非要复制,有没有什么办法呢?

三、JPG/WebP图片的解决之道

要想让JPG或者webp格式的图片也支持剪切板复制,很简单,转为PNG无损图就好了呀。

例如,绘制在canvas上,再使用canvas的toBlob()方法转一下就噢啦。

所以,上面提供的复制代码,我们还可以进一步增强下。

完整JS代码如下所示。

const doCopyImg2Clipboard = async (image, success = () => {}, failure = () => {}) =\> {
    if (!image || !image.src) {
        return;    
    }
    
    
    const { naturalWidth, naturalHeight } = image;
    
    if (!naturalWidth) {
        failure('图片尚未成功加载');
        
        return;
    }
    
    
    const canvas = document.createElement('canvas');
    canvas.width = naturalWidth;
    canvas.height = naturalHeight;
    
    const context = canvas.getContext('2d');
    
    context.drawImage(image, 0, 0, naturalWidth, naturalHeight);
    
    canvas.toBlob(blob => {
        
        const data = \[new ClipboardItem({
            \['image/png'\]: blob    
        })\];
        
        navigator.clipboard.write(data).then(success, failure);
    });
}

此时,无论是JPG图片还是WebP图片都可以复制到剪切板了。

doCopyImg2Clipboard(image, function () {
    console.log('复制成功');
});

有演示页面可以体验,您可以狠狠地点击这里:JS复制JPG图片到剪切板demo

然而,虽然图片复制了,但是变成PNG之后,原本尺寸适合的JPG图片尺寸就会变得很大。

那图片等于白压缩了。

所以,如果希望复制JPG图片的时候,保留该JPG原始的尺寸,该怎么办呢?

四、若想要保留原图尺寸?

解决方法是不是复制blob二进制数据,而是复制表示图片数据的base64字符串。

然后在上传或者粘贴的地方再二次处理。

图片转base64并复制到剪切板

不哔哔,直接看代码,使用FileReader对象将blob数据转为base64。

const doCopyImgBase64Clipboard = (image, success = () => {}, failure = () => {}) =\> {
    if (!image || !image.src) {
        return;    
    }
    
    
    const src = image.src;
    
    fetch(src).then(response => response.blob()).then(blob => {
        
        const reader = new FileReader();
        reader.onload = function() {
            navigator.clipboard.writeText(this.result).then(success, failure);
        };
        reader.readAsDataURL(blob) ;
    });
}

其中:

image

表示页面中的IMG元素对象。

success

表示成功的回调

failure

表示失败的回调

此时,就可以点击按钮,一键复制。

base64图片的处理

在这场场景下,复制并不是最难的,因为文本复制有着非常成熟的解决方案,比方说我之前弄了个“极简文字内容剪切板复制”的gitee项目,优点是自带复制成功的交互提示。

也可以使用业界知名的clipboard.js:https://github.com/zenorocha/clipboard.js

难点在于粘贴时候对base64文本的处理,因为处理粘贴文本并不是一个经常会遇到的场景,大多数的前端都缺乏相应的处理经验。

这里,通过两个例子,演示预览和上传的处理。

1. 预览

base64本身就可以作为图片的URL地址,因此,预览相对简单,只要创建个图片DOM元素,然后插入到页面中就好了。

那如何获得剪切板中粘贴的内容呢?有对应的API的,代码示意:


input.addEventListener('paste', event =\> {
    var paste = event.clipboardData?.getData('text') || '';
});

document.addEventListener('paste', event =\> {
    var paste = event.clipboardData?.getData('text') || '';
});

此时,只要判断 paste 内容匹配 base64 图片格式即可。

以在输入框中粘贴base64图片为例,完整交互JavaScript代码参见:


input.addEventListener('paste', event => {
    var clipboardData = event.clipboardData;

    var paste = clipboardData?.getData('text') || '';

    if (!/^data:image\\/\[a-z\]+;base64,/.test(paste)) {
        return;
    }
    
    event.preventDefault();
    
    
    const selection = document.getSelection();
    
    const range = selection.getRangeAt(0);
    
    range.deleteContents();
    
    const imgNode = document.createElement('img');
    imgNode.src = paste;
    range.insertNode(imgNode);
    
    range.setStartAfter(imgNode);
});

眼见为实,您可以狠狠地点击这里:JS复制图片base64信息到剪切板demo

点击按钮,会复制图片的base64信息,此时,再粘贴到页面中的测试输入框,会发现,显示的不是字符串内容,而是图片,就是因为有上面这段 JS 解析代码。

2. 上传

base64图片地址上传有两种解决方法,一是后端判断处理,即,如果post的是二进制数据流,如何处理,如果post的是字符串数据流,又该如何处理。

还有一种方法是在前端处理,将剪切板中的base64字符串转为blob文件数据再上传,这样,后端那边轻松点。

以下代码示意了下如何处理:


const b64toBlob = (b64Data, contentType='', sliceSize=512) =&gt; {
  const byteCharacters = atob(b64Data);
  const byteArrays = \[\];

  for (let offset = 0; offset &lt; byteCharacters.length; offset += sliceSize) {
    const slice = byteCharacters.slice(offset, offset + sliceSize);

    const byteNumbers = new Array(slice.length);
    for (let i = 0; i &lt; slice.length; i++) {
      byteNumbers\[i\] = slice.charCodeAt(i);
    }

    const byteArray = new Uint8Array(byteNumbers);
    byteArrays.push(byteArray);
  }

  const blob = new Blob(byteArrays, {type: contentType});
  return blob;
}

document.addEventListener('paste', function (event) {
    var clipboardData = event.clipboardData;

    var paste = clipboardData?.getData('text') || '';

    if (!paste.startsWith('data:image')) {
        return;
    }

    
    var base64 = paste.split('base64,')\[1\];
    
    var type = paste.split(';base64')\[0\].replace('data:', '');
    
    var blob = b64toBlob(base64, type);
    
    blob.name = new Date().toLocaleString().replaceAll('/', '-').replace(/\\s/g, '_').replaceAll(':', '') \+ '.' \+ (type.split('/')\[1\] || 'png');

    
});

于是,整个一套流程就可以走通了。

前端压缩图片,JS复制压缩后的base64字符信息,然后JS解析base64并上传。

结语

时不时给自己找个需求,而不仅仅是业务开发,可能会遇到平常遇不到的场景,积累不一样的经验。

要是下次业务开发遇到类似需求,那就是分分钟上手,开发效率领先一步。

还能撸一篇文章,完成自己给自己制定的平均每周需要一个技术文章输出的KPI。

一劳多得,岂不美哉。

噢啦,以上就是本文的全部内容啦。

希望里面的内容可以对大家的工作学习有所帮助。

如果您觉得不错,欢迎,点赞。

😋 😛 😝 😜 🤪

(本篇完) 是不是学到了很多?可以分享到微信!
有话要说?点击这里

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
javascript2026-02-24
navigator.sendBeacon全指南
在前端开发中,埋点系统是必不可少的一环。我们经常需要在用户 关闭页面 、 刷新 或 跳转路由 时,向服务器发送最后一条统计数据(比如用户停留时长、页面跳出率)。 但这看似简单的需求,在实现时却危机四伏:请求发不出去?页面跳转卡顿?今天我们就来聊聊这个问题的终极解决方案 —— 。 一、 痛点与传统方案的挣扎 场景还原 当用户点击关闭按钮时,浏览器会触发生命周期事件( 或 )。如果我们直接使用普通的异步 AJAX ( 或 ) 发送请求,浏览...
学习
javascript2025-11-02
理解浏览器事件系统,从用户点击到事件对象的完整旅程
深入理解浏览器事件系统:从用户点击到事件对象的完整旅程 “当我点击页面按钮时,背后发生了什么?为什么回调函数能收到一个包含丰富信息的event对象?今天,让我们一起揭开浏览器事件系统的神秘面纱。” 一个令人困惑的现象 作为前端开发者,我们每天都在写这样的代码: 这段代码如此熟悉,以至于我们很少停下来思考:​ ​这个 对象到底从哪里来?它为什么能知道点击的精确坐标?为什么能识别是哪个元素被点击了?​ ​ 更神奇的是,当我们手动创建事件时:...
学习
javascript2025-10-01
实现大文件上传全流程详解
在日常开发中,大文件上传是个绕不开的坎——动辄几百 MB 甚至 GB 级的文件,直接上传不仅容易超时,还会让用户体验大打折扣。最近我用 Vue+Express 实现了一套完整的大文件上传方案,支持分片上传、断点续传、秒传和手动中。 一、先看效果:我们要实现什么? 先上核心功能清单,确保大家明确目标,知道我们要解决哪些实际问题: 大文件分片上传 :将文件切成固定大小的小片段分批上传,避免单次请求超时 秒传 :服务器已存在完整文件时,直接返...
学习
javascript2025-09-18
JavaScript 的多线程能力:Worker
如果你写过一些计算量稍大的 JavaScript 代码,比如图像处理、大量数据排序或者复杂的算法,你几乎肯定遇到过浏览器“卡死”的现象。点击页面没反应,动画也停了,就像整个世界都静止了。 这就是主线程被阻塞的典型后果。因为主线程既要负责执行 JavaScript,又要负责渲染页面、响应用户操作,一旦它被繁重的计算任务占满,就无暇顾及其他,用户体验便直线下降。 这个问题的根源,正是“主线程是单线程的”。那么,如何解决呢? 答案很简单:把这...
学习面试
javascript2025-09-15
一张 8K 海报差点把首屏拖垮
你给后台管理系统加了一个「企业风采」模块,运营同学一口气上传了 200 张 8K 宣传海报。首屏直接飙到 8.3 s,LCP 红得发紫。 老板一句「能不能像朋友圈那样滑到哪看到哪?」——于是你把懒加载重新翻出来折腾了一轮。 解决方案:三条技术路线,你全踩了一遍 1\. 最偷懒:原生 一行代码就能跑,浏览器帮你搞定。 🔍 关键决策点 2020 年后现代浏览器全覆盖,IE 全军覆没。 必须写死 ,否则 CLS 会抖成 PPT。 适用场景...
学习
javascript2025-09-10
🚀 Web Worker让你的应用丝滑
🌟 引言 在日常的前端开发中,你是否遇到过这样的困扰: 大数据处理时页面卡死 :处理几万条数据时,页面直接卡成PPT,用户点击毫无反应 复杂计算阻塞UI :图片处理、数据分析等计算密集型任务让整个应用假死 文件上传/下载卡顿 :大文件操作时,其他功能完全无法使用 实时数据处理性能差 :WebSocket接收大量数据时,页面渲染严重滞后 今天分享6个Web Worker的核心技巧,让你的应用告别卡顿,用户体验丝滑如德芙! 💡 核心技巧...
学习

评论

请登录后发表评论

去登录
加载评论中...

目录