vue实现聊天+图片表情功能

本文详细描述了如何使用contenteditable属性实现类似微信聊天的输入框,支持文字与自定义表情组合发送,并解决添加表情时的焦点和位置问题。通过实例代码展示了如何在Vue应用中操作和渲染自定义表情。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目需求是这样的:要求实现类似于微信聊天一样,表情+文字效果 “文字效果😀😀😀”

表情包三种方案

表情包的实现其实可以分为以下三种情况:

  1. 表情包:点击表情--直接发送大表情(这种方案其实就是发送了一张定义好的图片而已)
  2. emoji图标表情:系统可识别的emoji图标表情,这种表情实现起来相对麻烦一些,其实这种方法emoji我们可以当成一个2位字符的特殊文字去处理
    • 推荐emoji网址:emojis
      • 案例:禾蛙招聘网站
  3. 自定义表情:一版是设计画好的,需要我们实现文字+表情进行发送和回显的
    • 思路1:我们在点击单个表情的时候输入框内需要添加表情,这是输入框内表情可以用‘[微笑]‘这种方式代替。只有在回显的时候去识别特殊表示,再用表情去替换掉即可
      • 案例:BOSS直聘网站
    • 思路2:输入框也需要是表情的回显,这种方法就相对麻烦了一些
      • 案例:微信客户端

实现

这里针对自定义表情包的思路2,进行实现,个人认为也是最麻烦的实现
实现效果
这里只针对输入框进行实现,对于数据传递给后端,消息的回显这里就不进行阐述了,想必大家也有自己更好的设计方法。

1、布局

大家可能会想到输入区域可以用用input,或者是textarea标签,但针对‘文字+自定义图片表情’是不太适用的,不过对于其他的实现方案还是可行的。
实现思路

  • 采用div+ contenteditable="true"属性
    • 举例:'

      这是一个可编辑段落。

      '

contenteditable属性:

  • 属性指定元素内容是否可编辑。
  • 所有主流浏览器都支持 contenteditable 属性
  • 设置了true:该标签默认就存在了@fcous,@input等事件函数
  • 注意: 当元素中没有设置 contenteditable 属性时,元素将从父元素继承。
描述
true指定元素是可编辑的
false指定元素是不可编辑的
    <div
      id="emojiText"
      contenteditable="true"
      ref="edit"
      placeholder="请输入内容"
    ></div>

2、实现

这里实现思路包括三个方向:

  1. 添加表情,添加文字
  2. 获取输入的消息内容,传递给后端
  3. 拿到特殊消息体'[微笑]',渲染对应的表情

添加表情,添加文字
输入文字:单纯的输入文字其实很简单,思路就是可编辑区域可输入对应的文本内容
添加表情:实现思路是,再点击添加表情时就是讲表情地址生成<img>标签插入到可编辑区域内
添加表情存在的问题:
问题1:添加表情时光标不会聚焦到可编辑区域
问题2:添加表情无法添加到光标定位的位置上
问题1解决方案
添加表情时检查是否聚焦,无聚焦执行fcous强制聚焦

let obj = this.$refs.edit; // obj 是可编辑的div
    let range, node;
    if (!obj.hasfocus) {
    obj.focus();
}

问题2解决方案
window.getSelection:选择的文本范围或光标的当前位置。
getRangeAt
返回选区开始的节点(Node)。
因为通常情况下用户只能选择一个范围,所以只有一个选区(range),此方法一般为getRangeAt(0)

range = window.getSelection().getRangeAt(0);
range.collapse(false); //光标移至最后

node = range.createContextualFragment(Img);
let c = node.lastChild;
range.insertNode(node);
if (c) {
    range.setEndAfter(c);
    range.setStartAfter(c);
}
let j = window.getSelection();
j.removeAllRanges();
j.addRange(range);

完整代码

<template>
  <div>
    <div
      id="emojiText"
      contenteditable="true"
      ref="edit"
      placeholder="请输入内容"
    ></div>
    <ul>
      <li v-for="item in emojis" :key="item.name">
        <img :src="item.path" alt="" @click="getEmojis" />
      </li>
    </ul>
    <button @click="senMsg">发送</button>
  </div>
</template>

<script>
export default {
  components: {},
  data() {
    return {
      emojis: [
        {
          name: "[emoji-1]",
          path: require("./assets/emojis/m1.png"),
        },
        {
          name: "[微笑]",
          path: require("./assets/emojis/m2.png"),
        },
        {
          name: "[emoji-3]",
          path: require("./assets/emojis/m3.png"),
        },
      ],
    };
  },
  mounted() {},
  methods: {
    getEmojis(event) {
      let Img = `<img src="${event.target.src}" style='height:10px;'>`;

      let obj = this.$refs.edit; // obj 是可编辑的div
      let range, node;
      if (!obj.hasfocus) {
        obj.focus();
      }
      /* 
        window.getSelection:选择的文本范围或光标的当前位置。
        getRangeAt
            返回选区开始的节点(Node)。
            因为通常情况下用户只能选择一个范围,所以只有一个选区(range),此方法一般为getRangeAt(0)
      */
      if (window.getSelection && window.getSelection().getRangeAt) {
        range = window.getSelection().getRangeAt(0);
        range.collapse(false); //光标移至最后

        node = range.createContextualFragment(Img);
        let c = node.lastChild;
        range.insertNode(node);
        if (c) {
          range.setEndAfter(c);
          range.setStartAfter(c);
        }
        let j = window.getSelection();
        j.removeAllRanges();
        j.addRange(range);
      } else if (document.selection && document.selection.createRange) {
        document.selection.createRange().pasteHTML(Img);
      }
    },
    senMsg() {
      console.log(this.$refs.edit.innerHTML);
    },
    natchMsg() {
      let str = "比如‘[微笑]’asdtgsaghd[微笑]ggg";
      let newStr = str;
      this.emojis.forEach((item) => {
        console.log(str.indexOf(item.name));
        if (str.indexOf(item.name) > -1) {
          let Img = `<img src="${item.path}" style='height:10px;'>`;
          newStr = str.replaceAll(item.name, Img);
        }
      });
      console.log(newStr);
    },
  },
};
</script>

<style lang='scss' scoped>
#emojiText {
  width: 600px;
  height: 300px;
  border: 1px solid #111;
}
ul {
  display: flex;
  list-style: none;
}
li img {
  width: 20px;
  height: 20px;
}
.msgicon {
  width: 10px;
  height: 10px;
}
</style>
``

### PC Vue 实现聊天界面并包含表情图片 在构建基于PC端的Vue聊天应用时,为了支持表情图片发送功能,通常会涉及多个组件技术栈。下面介绍一种可行的方法来创建这样的聊天界面。 #### 创建基础结构 首先定义基本HTML模板,在其中加入用于输入消息以及显示对话记录的部分: ```html <div id="app"> <!-- 聊天窗口 --> <div class="chat-window"> <ul v-for="(message, index) in messages" :key="index" class="messages-list"> {{ message.text }} <img v-if="message.type === 'image'" :src="message.src"/> </ul> <!-- 输入区域 --> <textarea @keyup.enter="sendMessage"></textarea> <!-- 表情选择面板 --> <button type="button" @click="toggleEmojiPicker">😊</button> <transition name="fade"> <emoji-picker ref="picker" v-show="showEmojiPicker" /> </transition> </div> </div> ``` 此部分代码展示了如何设置一个简单的聊天室布局,并预留了处理不同类型的消息(文本/图像),同时也加入了按钮用来触发表情选取器[^1]。 #### 添加表情包支持 对于表情的支持可以通过引入第三方库如`vue-emoji-mart`轻松实现。安装完成后按照官方文档配置好相应参数即可正常使用。当用户点击某个特定的表情图标时,则将其对应的Unicode字符追加到当前编辑区中去。 ```javascript import { Picker } from '@joeattardi/emoji-button'; // 初始化表情选择插件实例 const picker = new Picker({ style: { position: "absolute", bottom: "0px", right: "0px" }, }); methods:{ toggleEmojiPicker(){ this.showEmojiPicker=!this.showEmojiPicker; } } ``` 上述JavaScript片段说明了怎样利用外部工具快速集成表情符号的选择机制;同时提供了切换表情板可见性的逻辑控制函数[^2]。 #### 处理文件上传与预览 为了让用户体验更加友好,允许他们直接拖拽或浏览本地磁盘上的照片作为附件是非常必要的。这里推荐使用`element-ui`中的Upload组件或者其他类似的UI框架提供的解决方案。一旦接收到新的多媒体资源链接地址后,就把它封装成对象形式推送到消息队列里等待渲染。 ```css /* CSS样式调整 */ .messages-list img{ max-width: 200px; /* 控制最大宽度 */ } .fade-enter-active, .fade-leave-active { transition: opacity .5s ease-out; } .fade-enter-from, .fade-leave-to { opacity: 0; } ``` 最后给出了一些CSS样式的建议以便更好地适应不同尺寸的画面并且让动画效果更流畅自然。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

短暂又灿烂的

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值