Vue 中制作可以插入 emoji 表情的聊天输入框组件
一般在网页中见到的文本输入框的添加表情功能都是在用户选定表情后,在文本域中插入特定的字符代表一个表情。例如,:smile
代表一个微笑, 作为用户的我,体验起来就很不舒适,我就喜欢 QQ 聊天框这种,能够实时能够看到表情在输入框中被渲染出来的效果,所以在我自己写项目的时候直接就把这个功能实现了,本文讲解我是如何在 Vue 项目中实现文本域预览 emoji 表情这个功能的。
Emoji 表情库
巧妇难为无米之炊,造这个轮子第一步就是需要找到很多 emoji 表情,不然该如何下手呢?我找到了下面这个网站,非常给力,拥有所有平台实现 emoji 表情以及对应的 unicode 编码
template
在模板中使用 v-for
将 emoji 渲染出来, 我没有使用 textarea
标签, 是因为textarea
不能自适应高度,如果要自适应高度,又需要一个轮子,所以干脆直接使用 div
的 contenteditable
属性, 来模拟一个 textarea
好了:
<template>
<div class="area-box">
<div ref="area" contenteditable="true"></div>
<div class="send-box">
<i @click="emoji_show = !emoji_show"></i>
<div v-show="emoji_show" class="emotion-box">
<div class="emotion-list">
<i v-for="v in emoji" @click="addEmoji(v)">{{v}}</i>
</div>
</div>
</div>
</div>
</template>
script
主要问题在于如何在点击每个 emoji 表情字符时, 在文本域中光标所在位置插入相应的 emoji 字符,以及插入字符后,如何将光标正确定位。addEmoji
就是我的实现方法,详细的注释在每行都写了,没有太大难度:
<script>
export default {
data() {
return {
emoji_show: false,
emoji: ["😀", "😃", "😄", "😁", "😆", "😅", "🤣", "😂"],
}
},
methods: {
addEmoji(str) {
let area = this.$refs.area;
if (document.selection) {
area.focus();
const sel = document.selection.createRange();
sel.text = str;
sel.select();
} else if (area.selectionStart || area.selectionStart === '0') {// Mozilla/NETSCAPE support
const startPos = area.selectionStart,
endPos = area.selectionEnd,
// save scrollTop before insert
restoreTop = area.scrollTop;
area.innerText = area.innerText.substring(0, startPos) + str + area.innerText.substring(endPos, area.innerText.length);
if (restoreTop > 0) {
// restore previous scrollTop
area.scrollTop = restoreTop;
}
area.focus();
area.selectionStart = startPos + str.length;
area.selectionEnd = startPos + str.length;
} else {
const selection = window.getSelection();
// .nodeName === '#text'
if (selection.anchorNode && selection.anchorNode.nodeType === 3 && selection.anchorNode.parentNode === area) {// 光标选中
const range = selection.getRangeAt(0),
rangeStartOffset = range.startOffset,// 获取光标位置
textNode = range.startContainer;
if (!range.collapsed) {// 不是同一位置 代表选择了内容 则先删除选择的内容
range.deleteContents();
}
textNode.insertData(rangeStartOffset, str);// 文本节点在光标位置处插入新的表情内容
range.setStart(selection.anchorNode, rangeStartOffset + str.length);// 光标移动到表情的后面, 1个emoji的长度并不固定
range.collapse(true);// 光标开始和光标结束重叠
selection.removeAllRanges();// 清除选定对象的所有光标对象
selection.addRange(range);// 插入新的光标对象
} else {// 未出现光标 直接点击表情 默认插入最后位置
area.innerText += str;
selection.selectAllChildren(area);// 选择编辑器
selection.collapseToEnd();// 光标移动至最后
}
}
},
},
}
</script>
完整聊天框组件
以上的代码是关于核心功能的实现, 还有一些样式, 细节问题相关的代码非常多, 并没有展示出来, 我对此已经封装好了一个开箱即用的聊天框组件, 如果你觉得像我这样自己动手造轮子很麻烦, 那完全可以用我已封装好的这个组件:
安装
// 命令行
cnpm install vue-area
// main.js中:
import Editor from 'vue-area';
Vue.use(Editor);
使用
<editor :show="area" @send="send()" v-model="comment"></editor>
效果演示
这是一个我独立开发的单页面项目,街舞视频网站 唯舞,我在其中的评论功能使用了此组件,可以去尝试评论查看效果。