Vue实现输入框回车发送和粘贴文本与图片功能

目录
  • 实现回车发送
  • 粘贴文本
  • 粘贴图片
  • 总结

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

实现回车发送

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

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

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

event

<div class="input-box" contenteditable @keydown="handleKeydown"></div>
...
handleKeydown(event) {
    if (event.shiftKey && event.keyCode === 13) {
        document.execCommand('insertLineBreak'); // 换行
    } else if (event.keyCode === 13) { // 回车键
        console.log("回车发送");
        event.preventDefault();
        return false;
    }
}

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

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

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

按键修饰符

<div class="input-box" contenteditable @keyup.enter.exact="submit" @keydown.enter.exact="handleKeydown"></div>
...
},
submit() {
    console.log('回车发送');
},
handleKeydown(event) {
    event.preventDefault();
    return false;
},

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

优点

  • 使用方式简单
  • 可使用默认的方式换行

粘贴文本

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

paste.prevent

<div class="input-box" contenteditable @paste.prevent="onPaste"></div>
...
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; // 默认导出方法

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

注:本次复制的是<div data-v-66ea8951="">父组件修改子组件的值</div>

粘贴图片

粘贴图片和粘贴问题有相同之处,也有所不同。最大的不同是显示图片,需要获取图片的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 = /(?<=data:image).*?(?=;base64)/; // 正则表示时在用于replace时,根据浏览器的不同,有的需要为字符串
    base64Data = base64Data.replace(regx, '/gif');
  }
  canvas = null;
  ctx2 = null;
  return base64Data;
}
export default onPaste;

代码实现效果图:

插入图片可使用insertImage命令插入,也可以使用Selection来插入。效果都差不多

压缩图片,其实也就是用canvas根据尺寸比例,重新绘制一张图片,并获取图片的base64

返回数据时,需要返回压缩后和原图。

如果必须要压缩图片,则在handleImage方法中,直接使用FileReader加载图片后,将base64编码返回即可。

总结

本文实现了回车发送、粘贴文本、粘贴图片的功能

涉及事件修饰符、按键修饰符、剪贴板、canvas等知识

项目地址

以上就是Vue实现输入框回车发送和粘贴文本与图片功能的详细内容,更多关于Vue输入框粘贴文本图片的资料请关注我们其它相关文章!

(0)

相关推荐

  • 基于vue-simplemde实现图片拖拽、粘贴功能

    项目使用的是vue框架,需要一个markdown的编辑框,就在npm上找了一下,发现simplemde挺不错的,由于我比较懒,就顺便在npm又搜了一下,找到了vue-simplemde这个 package ,那就开始使用它吧. 但是这个 vue-simplemde 不支持图片拖拽上传.粘贴上传,也不能说是因为这个 vue-simplemde ,因为 vue-simplemde 只是对 simplemde 的基础上封装成一个Vue插件.所以最后还是由于 simplemde 没有提供相关的功能,但是

  • vue实现短信验证码输入框

    本文实例为大家分享了vue实现短信验证码输入框的具体代码,供大家参考,具体内容如下 先上最终效果 (此处代码显示的是短信验证码框的效果   其余部分并未放上去) html <div class="code"> <input id="first" class="inputStyle" v-model="code[0]" style="border-top-left-radius: 12px; bord

  • vue实现聊天框发送表情

    vue聊天框发送表情以及vue界面发送表情实现的具体代码,供大家参考,具体内容如下 1.在发送消息的时候,判断发送的消息是不是表情,表情的type:3,content:[爱心],这样存储在数据库中 2.在获取聊天记录的时候,判断type===3,处理表情, <img v-else-if="chatItem.type === 3" :src="emojiUrl + emojiMap[chatItem.content]" style="width:25p

  • vue中实现回车键登录功能

    created() { let that = this; document.onkeypress = function(e) { var keycode = document.all ? event.keyCode : e.which; if (keycode == 13) { that.login();// 登录方法名 return false; } }; } 以上的代码,在这几天测试发现有一个问题,在登录进系统之后,从首页切换到其他界面,点击回车,会导致界面调整到首页! 要实现: 只在Log

  • vue输入框使用模糊搜索功能的实现代码

    实现原理: 利用js的 indexOf 方法可返回某个指定的字符串值在字符串中首次出现的位置. 模板中的代码 <div class="search"> <!--输入框使用的是vant的输入框组件--> <van-search @input="autoSearch" v-model="value" placeholder="请输入搜索关键词" style="width:90%; displ

  • Vue实现输入框回车发送和粘贴文本与图片功能

    目录 实现回车发送 粘贴文本 粘贴图片 总结 上一篇中,我们初步新建了一个可“双向绑定”的聊天输入框,结合实际使用的场景,如果仅仅只是实现了“双向绑定”还不够,还必须处理粘贴文字.图片等功能.本文就在此基础上,继续探讨如何实现回车发送.粘贴文本(包括HTML).粘贴图片等功能.话不多说,咱们开整.contenteditable的元素如何实现“双向绑定”? 实现回车发送 在一个可编辑元素中,默认回车就是换行.但是如果我们要实现回车是“发送”的功能,需要怎么处理呢? 思路:监听键盘事件,如果判断用户

  • Java实现替换Word中文本和图片功能

    目录 前言 1.指定字符串内容替换文本 2.获取文档内容替换文本 3.图片替换文本 4.图片替换图片 前言 Word中的替换功能以查找指定文本然后替换为新的文本,可单个替换或全部替换.以下将要介绍的内容,除常见的以文本替换文本外,还将介绍使用不同对象进行替换的方法,具体可包括: 1. 指定字符串内容替换文本(通过方法replce(matchString, newValue, caseSensitive, wholeWord );直接指定替换的新字符串内容) 2. 获取文档内容替换文本(通过方法r

  • java微信企业号开发之发送消息(文本、图片、语音)

    上篇文章介绍了开启回调模式,开始回调模式后我们就要实现聊天功能了.平时使用微信聊天可以发送文本消息.语音.图片.视频等,这里只实现了其中的一些功能和大家分享. 一.与微信企业号建立连接 1.企业应用调用企业号提供的接口,管理或查询企业号后台所管理的资源.或给成员发送消息等,以下称主动调用模式. 2.企业号把用户发送的消息或用户触发的事件推送给企业应用,由企业应用处理,以下称回调模式. 3.用户在微信中阅读企业应用下发的H5页面,该页面可以调用微信提供的原生接口,使用微信开放的终端能力,以下称JS

  • 微信小程序实现聊天对话(文本、图片)功能

    本文实例为大家分享了微信小程序实现聊天对话功能的具体代码,供大家参考,具体内容如下 这是我实际项目线上的代码, 或许有些不足 || 和你的需求不符合. 上图: to_news.wxml <!--pages/index/to_news/to_news.wxml--> <view class='tab'> <view class='lan'>{{tabdata.title}}</view> <view class='tent'> <text&g

  • Vue实现输入框@功能的示例代码

    目录 前言 成员列表 创建 使用 输入框 获取光标的坐标 保存光标 插入文本 运行结果 总结 前言 前几篇文章中分别介绍了如何实现聊天输入框的双向绑定.回车键发送.粘贴文本图片等功能,本着完善输入框的目的,文本重点介绍聊天框如何实现@功能. 文章回顾: Vue实现contenteditable元素双向绑定的方法详解 Vue实现输入框回车发送和粘贴文本与图片功能 首先需要先理清思路: 成员列表组件,需要根据光标的位置调整,点击成员项时回调成员信息 获取光标的位置坐标(x值,y值) 输入框失焦时记录

  • 解决vue+element 键盘回车事件导致页面刷新的问题

    背景 今天发现输入框输入内容后回车就会刷新页面 解决 <el-form :inline="true" @submit.native.prevent> </el-form> el-from 加上 @submit.native.prevent 具体是参考element-ui文档解决的 W3C 标准中有如下规定: When there is only one single-line text input field in a form, the user agent

  • 解决Vue input输入框卡死的问题

    原因 我把 vuex 全局变量 $store.state.search.key 绑定到 v-model 上,然后在页面挂载时通过代码修改 $store.state.search.key 的值,最后在页面上输入任意值 input 框就卡住了 解决方法 1.input 标签增加 @input 属性 <input v-model='$store.state.search.key' @input="forceUpdateInput"> 2.methods 中增加对应方法 force

  • php使用str_replace实现输入框回车替换br的方法

    本文实例讲述了php使用str_replace实现输入框回车替换br的方法,分享给大家供大家参考.具体实现方法如下: 在我们用textarea时会发现回车与空格是不可看到的,所以我们利用str_replace函数将php中的\\n替换成br就可以了,有需要的朋友可以参考一下,代码如下: 复制代码 代码如下: function htmtocode($content) {     $content = str_replace("n", "<br>", str

  • JavaScript监听文本框回车事件并过滤文本框空格的方法

    本文实例讲述了JavaScript监听文本框回车事件并过滤文本框空格的方法.分享给大家供大家参考.具体如下: <script type="text/javascript" language="javascript"> var username = null; var password = null; //获取文本框 onload = function() { username = document.getElementById("txtUser

  • php发送html格式文本邮件的方法

    本文实例讲述了php发送html格式文本邮件的方法.分享给大家供大家参考.具体实现方法如下: <?php $to = "simon@mailexample.com, elaine@mailexample.com"; //设置收件人 $subject = "This is a test"; //设置E-mail主题 //设置E-mail内容: $message = " <html> <head> <title>Thi

随机推荐