关于一个复制粘贴的功能优化了两天这件事

上周接着优化我的设计器项目,其中复制粘贴这个小功能就花了大约两天时间,修复了一些问题,并且优化了使用上的体验,这篇文章就简单聊聊此功能的心路历程。

CV 是很常见且使用频率较高的快捷操作,本项目中复制粘贴的使用场景主要有以下几种:

  1. 直接粘贴文字
  2. 直接粘贴图片
  3. 复制画布中的元素粘贴
  4. 在编辑文字组件时粘贴文本

第三种项目中已经实现,最后一种是系统剪贴板自带的功能,不需要实现,主要实现的是前面两种,也就是读取系统剪贴板的操作

我们可以使用 Clipboard API 来调用剪贴板,调用时需要明确获得用户的许可。

系统剪贴板是属于托管浏览器的操作系统的数据缓冲区,用于文档或应用程序之间的短期数据存储或传输。事件是由于 cut、copy、paste 操作修改剪贴板而触发的。另一个访问剪贴板的方法:document.execCommand() 已被标记为弃用。

Clipboard API 的所有操作都是异步的,返回 Promise 对象,不会造成页面卡顿。而且,它可以将任意内容(比如图片)放入剪贴板中,以下是简单的读取和判断代码:

js 复制代码
navigator.clipboard
  .read()
  .then(async (dataTransfer) => {
    if (store.getters.dActiveElement.editable) {
      return;
    }
    for (let i = 0; i < dataTransfer.length; i++) {
      const item = dataTransfer[i];
      if (item.types.toString().indexOf("image") !== -1) {
        const imageBlob = await item.getType(item.types[0]);
        const file = new File([imageBlob], "screenshot.png", {
          type: "image/png",
        });
        // TODO: 上传图片 file
        // TODO: 添加图片到画布中
        break;
      } else if (item.types.toString().indexOf("text") !== -1) {
        // TODO: 添加文字到画布中
        break;
      }
    }
  })
  .catch((error) => {
    // 剪贴板内容为空
  });

可以使用 navigator.clipboard.writeText('') 来清空系统剪贴板的文字,但如果复制的是图片,数据就清除不掉,所以我通过伪造一个空字符数据来实现清空剪贴板图片的效果:

js 复制代码
navigator.clipboard.write([
  new ClipboardItem({
    "text/plain": new Blob([""], { type: "text/plain" }),
  }),
]);

文字 CV 效果如下,可将任意文字内容复制粘贴进画布。

由于图片自动上传,不会触发用户图片列表的刷新,一开始我想干脆把图片列表放进全局状态管理,但很快发现这么做很麻烦,虽然图片列表的数据可以在其它地方更新了,但是它还有分页数要处理(keepalive 的组件),所以最后还得是事件广播的方式来解决最好。

由于 Vue3 中已经移除了全局的 eventBus 功能,所以安装了官方推荐的 mitt 这个库来实现。

github.com/developit/m...

粘贴之后还要将新的元素再复制一遍,这也是一个小优化点,体现无限复制下去的效果。

最后还有一个小细节:每次粘贴图片,都会触发上传,稍不注意就会上传许多重复的图片,所以这里我也做了相应的优化,从系统剪贴板读取的图片数据会在粘贴一次后转化为画布的图片元素,此时再自动执行一次复制,这样多次执行粘贴操作只会上传一次图片

如果不做这个优化会是怎么样的呢?下面有请我们的老朋友稿定设计来演示下,可以看到多次粘贴,就会造成重复上传:

接着我也看了 Canva(可画)的表现,看来业界都是相当统一,不过可画这边的上传队列是非异步的,所以不停粘贴的话甚至会卡住,就是一定得等前面的图片上传完成(我这里是连续按 Ctrl V 的,却一直在等待):

另外可画这个上传时的水位图动画蛮有趣,以前一直觉得他家界面有点土,然而这种小细节做出来着实平添了几分科技感,值得学习。

创客贴的也体验了一下,实在太粗糙,就不放演示了😅。

最后欢迎访问迅排设计在线体验,文章有点水,纯当一个小分享,希望对你有所帮助。

相关推荐
Csvn33 分钟前
🕰️ 闭包 + setTimeout 的 5 个经典陷阱:为什么定时器看到的永远不是最新的值?
前端
lilian2331 小时前
Harmony os 技术实战|拼豆制图27:用单字符编码承载 50 张 70×70 图纸
前端·数据库·华为·harmonyos
CarIise1 小时前
CSS选择器与样式关联
前端·css·tensorflow
里欧跑得慢3 小时前
CSS 模块化架构的演进:BEM、CSS Modules 到 CSS-in-JS 的反思
前端·css·flutter·web·css-in-js
IT_陈寒3 小时前
Vue的computed属性把我坑惨了,原来我一直用错姿势
前端·人工智能·后端
小灰灰搞电子3 小时前
Rust+Slint 实现温度计源码分享
前端·rust·slint
计算机魔术师4 小时前
面壁智能 OpenBMB 推出 MathForm,面向 Lean 4 数学自动形式化的开源框架、数据集与模型
前端
NeilCarmack4 小时前
Deepseek-harness增加桌面版端序列:第 2 讲 · spawn Electron:当前进程如何“交棒“
前端·javascript·electron
上海魁鲸科技有限公司5 小时前
APS高级排产系统到底有什么用?一文讲清功能、选型与落地建议
前端·microsoft·excel
陈随易5 小时前
Bun v1.4 更新总结:把浏览器、图片、定时任务和工程工具都装进一个运行时
前端·后端·程序员