聊天输入框如何实现回车发送、粘贴文本和图片? 您所在的位置:网站首页 js如何添加图片和文字 聊天输入框如何实现回车发送、粘贴文本和图片?

聊天输入框如何实现回车发送、粘贴文本和图片?

2023-12-22 22:38| 来源: 网络整理| 查看: 265

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情。

上一篇中,我们初步新建了一个可“双向绑定”的聊天输入框,结合实际使用的场景,如果仅仅只是实现了“双向绑定”还不够,还必须处理粘贴文字、图片等功能。本文就在此基础上,继续探讨如何实现回车发送、粘贴文本(包括HTML)、粘贴图片等功能。话不多说,咱们开整。contenteditable的元素如何实现“双向绑定”? - 掘金 (juejin.cn)

实现回车发送

在一个可编辑元素中,默认回车就是换行。但是如果我们要实现回车是“发送”的功能,需要怎么处理呢?

思路:监听键盘事件,如果判断用户按下的是回车键,则执行发送逻辑,并阻止浏览器的默认行为。

实现起来也可以有两种方式:

event

... handleKeydown(event) { if (event.shiftKey && event.keyCode === 13) { document.execCommand('insertLineBreak'); // 换行 } else if (event.keyCode === 13) { // 回车键 console.log("回车发送"); event.preventDefault(); return false; } }

这样就可以实现回车发送。但是这种方式是有问题的,具体如下:

如果持续按下回车键,将会一直触发 其他键 + 回车键也会触发此判断 需要额外增加逻辑,否则输入框将不可换行

那有没有什么办法是可以解决的呢,对于Vue项目来说,可以借助按键修饰符

按键修饰符

... }, submit() { console.log('回车发送'); }, handleKeydown(event) { event.preventDefault(); return false; },

按键修饰符的具体文档可参考官网:事件处理 — Vue.js (vuejs.org),本处只重点说明.extra修饰符。.extra可以精确控制按键组合,结合keyup和keydown,上述代码翻译过来就是:只有一个回车键时,并且回弹起来才会触发“回车发送”;如果只有一个回车键,并且按下,则阻止浏览器默认事件。这样就可以实现“回车发送”。也许有人会问,换行怎么办。答案就是使用shift + enter,这也是默认的换行。如果不喜欢shift+enter就需要自己处理换行了。

优点

使用方式简单 可使用默认的方式换行 粘贴文本

粘贴文本不是直接使用ctrl + v嘛,这都不知道?话是这么说,但是如果我们相对粘贴的文本做些处理,直接使用ctrl + v就不合适了,而且后面还会用来粘贴图片。想要直接粘贴图片并显示,还没有原生输入框可做到吧。本处需要使用paste事件,也就是专门处理粘贴的。由于我们是要手动插入数据,因此需要阻止默认操作,需要借助prevent事件修饰符。

paste.prevent

... import onPaste from '@/utils/onPaste.js'; // 引入专门处理粘贴的方法 ... // 注意是异步处理 async onPaste(event) { const result = await onPaste(event); // 传入粘贴事件 console.log('处理后的数据', result); // 插入文本   document.execCommand('insertText', false, result.data); }

重点来了

新建onPaste.js,用来处理文本和图片的粘贴

// onPaste.js // 定义粘贴函数 const onPaste = (event) => { // 剪贴板没数据,则直接返回 if (!event.clipboardData || !event.clipboardData.items) {   return; } // 封装Promise return new Promise((resovle, reject) => {   // 遍历剪贴板   for(let i = 0, len = event.clipboardData.items.length; i < len; i++) {     const item = event.clipboardData.items[i];     if (item.kind === 'string') {       // 方式一,直接返回粘贴的文本数据       // let str = event.clipboardData.getData('text');       // resovle({       //   data: str       // });       // 方式二,过滤掉粘贴的文本中存在的html标签和换行等字符       let reg = //g; // 匹配粘贴文本中的html标签       // 处理字符串类型,参数为回调函数       item.getAsString(str => {         resovle({           data: str.replace(reg, '').replace(/(\r\n)|(\n)/g, '') // 去掉换行符         })       })     } else {       reject(new Error('不支持粘贴该类型'));     }   } }) } export default onPaste; // 默认导出方法

关于剪贴板,由几个概念需要提前了解:clipboardData、DataTransfer、DataTransferItem,具体可参考clipboardData。运行结果如下:

paste-文本.png

注:本次复制的是父组件修改子组件的值

粘贴图片

粘贴图片和粘贴问题有相同之处,也有所不同。最大的不同是显示图片,需要获取图片的src。在写代码前先想几个问题:

图片展示需要提供src,那么如何获取src 图片可大可小,如何限制图片的尺寸,避免超出输入框范围 图片像素很高,是否需要压缩

有了这些问题,就需要一一解决。

图片的压缩,采用canvas绘制指定尺寸的图片 base64就由canvas的toDataURL方法获得 压缩时,维持图片的宽高比

话不多说,先看代码:

// DivEditable.vue async onPaste(event) {     const result = await onPaste(event);     console.log('处理后的粘贴数据', result);     const imgRegx = /^data:image/png|jpg|jpeg|gif|svg|bmp|tif/; // 支持的图片格式     if (imgRegx.test(result.compressedDataUrl)) {     // document.execCommand('insertImage', false, result.compressedDataUrl);       const sel = window.getSelection(); // 获取当前光标位置       if (sel && sel.rangeCount === 1 && sel.isCollapsed) {         const range = sel.getRangeAt(0);         const img = new Image();         img.src = result.compressedDataUrl; // 使用压缩后的图片         range.insertNode(img);         range.collapse(false);         sel.removeAllRanges();         sel.addRange(range);       }     } } // onPaste.js // 定义粘贴函数 const onPaste = (event) => { ... return new Promise((resovle, reject) => {   for(let i = 0, len = event.clipboardData.items.length; i < len; i++) {     const item = event.clipboardData.items[i];     if (item.kind === 'file') {       const file = item.getAsFile();       if (item.type.match('^image/')) {         // 处理图片         handleImage(file, (data) => {           resovle(data)         })       } else {         // 其他文件直接返回         resovle({           data: file,           type: 'file'         })       }     } else {       reject(new Error('不支持粘贴该类型'));     }   } }) } function handleImage(file, callback, maxWidth = 200) { console.log('粘贴的图片', file); if (!file || !//(?:png|jpg|jpeg|gif)/i.test(file.type)) {   console.log('图片格式不支持');   return; } const reader = new FileReader(); reader.onload = function () {   const result = this.result;   console.log('compressedDataUrl', result);   let img = new Image();   img.onload = function() {     let compressedDataUrl = compress(img, file.type, maxWidth, true);     let url = compress(img, file.type, maxWidth, false);     img = null;     callback({       data: file,       compressedDataUrl,       url,       type: 'image'     })   }   img.src = result; }; reader.readAsDataURL(file); } function compress(img, type, maxWidth, flag) { let canvas = document.createElement('canvas'); let ctx2 = canvas.getContext('2d'); ​ let ratio = img.width / img.height; let width = img.width, height = img.height; // 根据flag判断是否压缩图片 if (flag) {   // 压缩后的图片展示在输入框   width = maxWidth;   height = maxWidth / ratio; // 维持图片宽高比 } canvas.width = width; canvas.height = height; ​ ctx2.fillStyle = '#fff'; ctx2.fillRect(0, 0, canvas.width, canvas.height); ctx2.drawImage(img, 0, 0, width, height); ​ let base64Data = canvas.toDataURL(type, 0.75); ​ if (type === 'image/gif') {   let regx = /(?


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有