From 1038f9300727a839dfaef2f2c2c53c5deb8484fc Mon Sep 17 00:00:00 2001 From: LCJ-MinYa <1049468118@qq.com> Date: Mon, 23 Jun 2025 15:27:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9B=BE=E7=89=87=E6=A0=87=E6=B3=A8?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=8F=B3=E9=94=AE=E5=8A=9F=E8=83=BD=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/image-editor.vue | 87 ++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/src/views/demo/tuiImageEditor/components/image-editor.vue b/src/views/demo/tuiImageEditor/components/image-editor.vue index 82f8acc..07c164b 100644 --- a/src/views/demo/tuiImageEditor/components/image-editor.vue +++ b/src/views/demo/tuiImageEditor/components/image-editor.vue @@ -31,6 +31,11 @@ @click="changeImage('next')" > + + @@ -251,6 +256,62 @@ const initTuiImageEditor = () => { // 针对图片加载首次不显示,必须手动调用一次refreshImageShow方法(个人项目未复现,公司项目有该问题,考虑是返回的图片的加载方式问题或者格式等问题) // refreshImageShowWithDelay(300, true); + + /** + * canvas禁用右键功能 + * 该组件默认有两层canvas,第一层图片canvas在底部,画布canvas在顶部,需要禁用右键功能(如果不禁用,会导致默认右键复制和保存的图片是画布这一层,空白的图片) + * 再自定义右键功能,实现图片下载和复制到剪贴板功能 + */ + const canvas = document.querySelector('.upper-canvas'); + const customContextMenu = document.querySelector('#customContextMenu'); + canvas.addEventListener('contextmenu', (e) => { + console.log(customContextMenu); + e.preventDefault(); + + // 显示自定义菜单 + customContextMenu.style.display = 'block'; + customContextMenu.style.left = e.pageX + 'px'; + customContextMenu.style.top = e.pageY + 'px'; + }); + // 点击其他地方隐藏菜单 + document.querySelector('.tui-image-editor-wrap').addEventListener('click', () => { + customContextMenu.style.display = 'none'; + }); +}; + +const saveCanvasAsImage = () => { + const canvas = document.querySelector('.lower-canvas'); + const a = document.createElement('a'); + a.href = canvas.toDataURL('image/jpeg'); + a.download = new Date().getTime() + '.jpg'; + a.click(); + a.remove(); +}; + +// 复制图片到剪贴板功能如果是以iframe嵌入,必须要在嵌入加上属性才可以正常使用 +const copyCanvasToClipboard = async () => { + const canvas = document.querySelector('.lower-canvas'); + /** + 问题的现象 + 在使用浏览器的剪贴板API的 write 方法尝试将JPEG图像写入剪贴板时,我们可能会遇到这样的错误:DOMException: Failed to execute 'write' on 'Clipboard': Type image/jpeg not supported on write. + 问题的原因 + 这个错误的产生主要是因为 write 方法目前只支持 text/plain 和 text/html MIME 类型,这意味着你不能直接将JPEG图像写入剪贴板。然而,为什么浏览器的剪贴板API会有这样的限制呢? + 在深入浏览器的内部实现之前,我们先来了解一下剪贴板API。剪贴板API是浏览器提供的一种接口,用于访问用户的剪贴板。它的主要目的是为了提供一种简单、一致的方式来读取和写入剪贴板数据。 + 然而,由于安全和隐私的考虑,浏览器的剪贴板API并不允许直接访问剪贴板中的所有类型的数据。例如,它不允许访问剪贴板中的图像数据,除非用户明确地进行了粘贴操作。这就是为什么我们不能直接将JPEG图像写入剪贴板的原因。 + 解决方案 + 那么,我们应该如何解决这个问题呢?一种可能的解决方案是将JPEG图像转换为PNG图像,然后将其写入剪贴板。这个方法可能不适用于所有浏览器,因为不是所有浏览器都支持 clipboard.write 方法或 ClipboardItem 对象。但是,我将提供的代码已经在大多数现代浏览器中进行了测试,并且可以正常工作。 + */ + // 这里图片格式只能使用png,原因如上 + const blob = await new Promise((resolve) => { + canvas.toBlob(resolve, 'image/png'); + }); + if (navigator.clipboard && navigator.clipboard.write) { + await navigator.clipboard.write([ + new ClipboardItem({ + 'image/png': blob, + }), + ]); + } }; const addEventListenerFN = () => { @@ -266,7 +327,8 @@ const handleClickOutside = (event) => { if ( getBtnDom('.save-btn').contains(event.target) || getBtnDom('.left-btn').contains(event.target) || - getBtnDom('.right-btn').contains(event.target) + getBtnDom('.right-btn').contains(event.target) || + getBtnDom('#customContextMenu').contains(event.target) ) { return; } @@ -472,4 +534,27 @@ body > svg:last-of-type { .tui-image-editor-container div.tui-colorpicker-clearfix { display: none !important; } + +#customContextMenu { + position: absolute; + display: none; + background-color: #fff; + padding: 10px 0; + border-radius: 5px; + + li { + box-sizing: border-box; + cursor: pointer; + font-size: 14px; + color: #666; + width: 100%; + height: 30px; + line-height: 30px; + padding: 0 10px; + } + + li:hover { + background-color: #f5f5f5; + } +}