目录
功能描述
图片的格式
复制图片和文字
第一种,直接复制(不推荐)
第二种,画图后复制
下载图片和文字
总结
功能描述
可以简单描述成复制图片和文字,下载图片和文字
前者还好说,比如在图片右键点击,就有浏览器中的复制图片的功能,如下:
所以实现方法应该挺多,而后者如果想要一张图片和文字说明都被下载,那肯定是在一起,下载成一张图片的样子,所以后者的实现方法,我暂时只想到一种,画canvas然后下载。
前者的实现,我有两种,各有优缺点。
说一下,我需要实现的样子,如上面的动图,我是开发了一个弹窗,在弹窗里写了一个图片和一些文字,然后底部有一个复制和下载按钮。
PS:之前考虑使用富文本,因为富文本可以直接粘贴图片和文字到微信输入框和文档,可是一个简单的功能不考虑引入富文本了,比较麻烦。
另外,关于复制和粘贴图片和文字,首先要知道一点,在微信聊天框和文档等富文本环境里直接粘贴图像的支持取决于这些平台的实现。通常情况下,直接从剪贴板中复制图像并粘贴到富文本环境中并不能让图像显示。微信聊天框和文档通常期望用户通过上传图片的方式来插入图片,而不是直接从剪贴板中粘贴。这样可以更好地控制图像的来源和确保安全性。
图片的格式
可以是任何有效的图片 URL,包括基于网络的 URL、base64 编码的数据 URI 或其他支持的格式。只要
img.src
可以成功加载图片即可。我用的是el-image以下是一些示例:
网络图片 URL:
onst imageUrl = 'https://example.com/path/to/image.jpg';
Base64 编码的图片:
const imageUrl = 'data:image/png;base64,iVBORw...'; // 替换为实际的 base64 编码
其他格式的图片 URL:
const imageUrl = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><rect width="100%" height="100%" fill="red" /></svg>';
无论使用哪种格式,只要能够被
Image
对象成功加载,就可以用于在canvas
中绘制。在实际使用中,确保Url
是有效的,不会导致跨域问题(如果是网络图片),并且可以被浏览器加载。
复制图片和文字
图片和文字,一般是图片和文字说明,如果只是复制,那么一般需要能够在文档,微信聊天的输入框等地方能够粘贴,实现这种就可以了,那也就是要么将这个带图片和文字的DOM复制下来,要么也就是跟下载一样,画一个canvas ,然后复制。二选一,详细描述在下面。
所以有几个能复制的API就不太使用,比如:浏览器的 Clipboard
,提供了它 ClipboardItem
,但并不是所有浏览器都支持将图像粘贴到富文本编辑器中。
第一种,直接复制(不推荐)
第一种方式是先把图片和文字的DOM给一个ref,然后就复制这个dom,调用这document.execCommand,就能实现,代码如下:
<div class="image-container" style="flex-direction: column;" ref="imageDom"><el-imagestyle="width: 200px; height: 200px":src="url"fit="fill"></el-image> <span class="text">{{ida}}</span></div>
this.$nextTick(() =>{//nextTick,当前dom渲染完毕的回调//打印获取的domconsole.log('imageDom', this.$refs.imageDom) const selection = window.getSelection()const range = document.createRange()//传入domrange.selectNode(this.$refs.imageDom) selection.addRange(range)document.execCommand('copy') //document.execCommand 已经被废弃// 清除缓存selection.removeAllRanges()this.$Message.success("复制成功!");})
实现效果如下,点击复制后到一个文档,点击粘贴,内容如下:
白色的背景估计是dom的背景色,也粘贴下来了,并且我在Dom里设置图片大小是200px高宽,这个粘贴的是原始大小,我是缩小了后才能看到这样子的。
但是!这种方法虽然可以在文档里粘贴,但是微信输入框不可以!只能粘贴文字:
所以我不推荐的原因有两个,
1.不能在聊天输入框里粘贴的话,那么这个复制功能应用场景就比较局限
2.不能保留DOM的样式,比如无法粘贴文本的字体大小颜色之类的,有些富文本的可以。
第二种,画图后复制
用Canvas画出图像和文本,然后通过 Clipboard API 复制到剪贴板。这种方式可以确保在粘贴时能够包含图像和文本。这个需要注意一下浏览器兼容性, Canvas、Blob 和 Clipboard API 的使用在大多数现代浏览器中是支持的,但在一些较老的浏览器中可能存在问题。建议在目标浏览器上进行测试,确保功能正常。
不过在移动端设备上,可能需要特别小心兼容性问题。如果在某些浏览器或环境中发现问题,可以考虑降级到一种更普遍支持的方案,如直接下载图片和文本的合并文件,然后手动上传,正好下下面也考虑开发下载功能。
this.$nextTick(() => {// 获取图片和文本const imageUrl = 'https://file.iviewui.com/images/image-demo-13.jpg'; // 替换为实际的图片URLconst text = '123456';// 创建一个 canvas 元素const canvas = document.createElement('canvas');const ctx = canvas.getContext('2d');// 创建一个 Image 元素,用于加载图片const img = new Image();img.crossOrigin = 'Anonymous'; // 处理跨域图片img.src = imageUrl;img.onload = () => {// 设置 canvas 的尺寸const canvasWidth = img.width;const canvasHeight = img.height + 50; // 考虑到文字的高度,增加 50px 的高度canvas.width = canvasWidth;canvas.height = canvasHeight;// 绘制白色背景ctx.fillStyle = '#ffffff';ctx.fillRect(0, 0, canvasWidth, canvasHeight);// 绘制图片ctx.drawImage(img, 0, 0, canvasWidth, img.height);// 绘制文本ctx.font = 'bold 14px Arial';ctx.fillStyle = '#000';ctx.textAlign = 'center'; // 文本水平居中ctx.textBaseline = 'top';// 设置文本距离图片底部的边距const textMargin = 10;const textX = canvasWidth / 2; // 文本横向居中const textY = img.height + textMargin;ctx.fillText(text, textX, textY);// 复制 canvas 内容到剪贴板canvas.toBlob((blob) => {const clipboardItem = new ClipboardItem({ 'image/png': blob });navigator.clipboard.write([clipboardItem]).then(() => {console.log('Canvas content copied to clipboard');});});};});
这里画图时,我是给图片设置了一个宽度200px,画布高250px,所以复制下的图片就100+KB,原图可能几MB,也可以不压缩,直接在原图的高宽上加一个文字的高度就行,但是这种画出来的图不协调。
上面是复制的时候去画图,当然也可以选择打开弹窗时就显示一张画好的图,然后点击复制直接复制。其实就是上面的代码拆开,然后加了一个canvas的dom,可以不看。
<canvas ref="canvas"></canvas>
// 打开弹窗时this.$nextTick(() => {const canvas = this.$refs.canvas;this.drawCanvas(canvas);});
// 画图
drawCanvas(canvas) {const ctx = canvas.getContext('2d');const img = new Image();img.crossOrigin = 'Anonymous';img.src = this.url;img.onload = () => {const canvasWidth = 400;const canvasHeight = 400 + 50;canvas.width = canvasWidth;canvas.height = canvasHeight;ctx.fillStyle = '#ffffff';ctx.fillRect(0, 0, canvasWidth, canvasHeight);ctx.drawImage(img, 0, 0, canvasWidth, 400);ctx.font = 'bold 14px Arial';ctx.fillStyle = '#000';ctx.textAlign = 'center';ctx.textBaseline = 'top';const textMargin = 10;const textX = canvasWidth / 2;const textY = 400 + textMargin;ctx.fillText(this.ida, textX, textY);};},// 复制
// 在点击复制时将 Canvas 的内容复制到剪贴板const canvas = this.$refs.canvas;canvas.toBlob((blob) => {const clipboardItem = new ClipboardItem({ 'image/png': blob });navigator.clipboard.write([clipboardItem]).then(() => {console.log('Canvas content copied to clipboard');});});
实现效果如下:
我是先粘贴到微信聊天框,然后粘贴到文档里,粘贴后的图片大小压缩了一部分,如果只是看图片的话,这种还可以看,压缩的不太过分。
下载图片和文字
下载是将canvas 复制然后通过<a>标签创建链接在新标签页下载的手段,比较常见。
// 复制 canvas 内容const canvas = this.$refs.canvas;canvas.toBlob((blob) => {const clipboardItem = new ClipboardItem({ 'image/png': blob });navigator.clipboard.write([clipboardItem]).then(() => {this.$Message.info('下载中...')});});// 创建下载链接const link = document.createElement('a');link.href = canvas.toDataURL('image/png');link.download = 'canvas_image.png';// 将链接添加到页面document.body.appendChild(link);// 触发链接点击事件const clickEvent = new MouseEvent('click', {bubbles: true,cancelable: false,view: window});link.dispatchEvent(clickEvent);// 移除链接document.body.removeChild(link);
总结
暂时只想到这两种办法,然后基本都是通过HTML5的
Clipboard
来实现复制。
核心代码:
canvas.toBlob((blob) => {const clipboardItem = new ClipboardItem({ 'image/png': blob });navigator.clipboard.write([clipboardItem]).then(() => {this.$Message.info('下载中...')});});
Canvas API:
canvas.toBlob
方法用于将 Canvas 上的内容转换成一个 Blob 对象。在你的代码中,blob
就是包含 Canvas 内容的 Blob 对象。Blob 是二进制大对象(Binary Large Object)的缩写,它是一个表示任意类型数据的不可变、原始数据的类文件对象。在这里,blob
包含了 Canvas 上绘制的图像和文本。canvas.toBlob((blob) => { // 在这里,blob 包含了 Canvas 上绘制的图像和文本 });
Clipboard API:
ClipboardItem
是 Clipboard API 中的一个对象,它表示要复制到剪贴板的数据项。在这里,创建了一个包含图像的 ClipboardItem 对象,并指定了 MIME 类型为'image/png'
。然后,通过navigator.clipboard.write
方法将 ClipboardItem 对象写入剪贴板。const clipboardItem = new ClipboardItem({ 'image/png': blob }); navigator.clipboard.write([clipboardItem]).then(() => { // 复制到剪贴板成功后的回调 });
在这个过程中,浏览器会向用户请求权限来访问剪贴板,因此需要在用户同意后执行复制操作。
总体而言,这段代码的目的是在 Canvas 中绘制图像和文本,将绘制的内容转换成 Blob 对象,然后通过 Clipboard API 将 Blob 对象复制到剪贴板中。在这个过程中,用户可能会看到有关剪贴板操作的浏览器提示。OVER。