vue實(shí)現(xiàn)聊天+圖片表情功能
項(xiàng)目需求是這樣的:要求實(shí)現(xiàn)類似于微信聊天一樣,表情+文字效果 “文字效果??????”
表情包三種方案
表情包的實(shí)現(xiàn)其實(shí)可以分為以下三種情況:
- 表情包:點(diǎn)擊表情--直接發(fā)送大表情(這種方案其實(shí)就是發(fā)送了一張定義好的圖片而已)
- emoji圖標(biāo)表情:系統(tǒng)可識(shí)別的emoji圖標(biāo)表情,這種表情實(shí)現(xiàn)起來(lái)相對(duì)麻煩一些,其實(shí)這種方法emoji我們可以當(dāng)成一個(gè)2位字符的特殊文字去處理
- 推薦emoji網(wǎng)址:emojis
- 案例:禾蛙招聘網(wǎng)站
- 推薦emoji網(wǎng)址:emojis
- 自定義表情:一版是設(shè)計(jì)畫好的,需要我們實(shí)現(xiàn)文字+表情進(jìn)行發(fā)送和回顯的
- 思路1:我們?cè)邳c(diǎn)擊單個(gè)表情的時(shí)候輸入框內(nèi)需要添加表情,這是輸入框內(nèi)表情可以用‘[微笑]‘這種方式代替。只有在回顯的時(shí)候去識(shí)別特殊表示,再用表情去替換掉即可
- 案例:BOSS直聘網(wǎng)站
- 思路2:輸入框也需要是表情的回顯,這種方法就相對(duì)麻煩了一些
- 案例:微信客戶端
- 思路1:我們?cè)邳c(diǎn)擊單個(gè)表情的時(shí)候輸入框內(nèi)需要添加表情,這是輸入框內(nèi)表情可以用‘[微笑]‘這種方式代替。只有在回顯的時(shí)候去識(shí)別特殊表示,再用表情去替換掉即可
實(shí)現(xiàn)
這里針對(duì)自定義表情包的思路2,進(jìn)行實(shí)現(xiàn),個(gè)人認(rèn)為也是最麻煩的實(shí)現(xiàn)
實(shí)現(xiàn)效果
這里只針對(duì)輸入框進(jìn)行實(shí)現(xiàn),對(duì)于數(shù)據(jù)傳遞給后端,消息的回顯這里就不進(jìn)行闡述了,想必大家也有自己更好的設(shè)計(jì)方法。

1、布局
大家可能會(huì)想到輸入?yún)^(qū)域可以用用input,或者是textarea標(biāo)簽,但針對(duì)‘文字+自定義圖片表情’是不太適用的,不過對(duì)于其他的實(shí)現(xiàn)方案還是可行的。
實(shí)現(xiàn)思路
- 采用
div+ contenteditable="true"屬性- 舉例:'
這是一個(gè)可編輯段落。
'
- 舉例:'
contenteditable屬性:
- 屬性指定元素內(nèi)容是否可編輯。
- 所有主流瀏覽器都支持 contenteditable 屬性
- 設(shè)置了true:該標(biāo)簽?zāi)J(rèn)就存在了@fcous,@input等事件函數(shù)
- 注意: 當(dāng)元素中沒有設(shè)置 contenteditable 屬性時(shí),元素將從父元素繼承。
| 值 | 描述 |
|---|---|
| true | 指定元素是可編輯的 |
| false | 指定元素是不可編輯的 |
<div
id="emojiText"
contenteditable="true"
ref="edit"
placeholder="請(qǐng)輸入內(nèi)容"
></div>
2、實(shí)現(xiàn)
這里實(shí)現(xiàn)思路包括三個(gè)方向:
- 添加表情,添加文字
- 獲取輸入的消息內(nèi)容,傳遞給后端
- 拿到特殊消息體'[微笑]',渲染對(duì)應(yīng)的表情
添加表情,添加文字
輸入文字:?jiǎn)渭兊妮斎胛淖制鋵?shí)很簡(jiǎn)單,思路就是可編輯區(qū)域可輸入對(duì)應(yīng)的文本內(nèi)容
添加表情:實(shí)現(xiàn)思路是,再點(diǎn)擊添加表情時(shí)就是講表情地址生成<img>標(biāo)簽插入到可編輯區(qū)域內(nèi)
添加表情存在的問題:
問題1:添加表情時(shí)光標(biāo)不會(huì)聚焦到可編輯區(qū)域
問題2:添加表情無(wú)法添加到光標(biāo)定位的位置上
問題1解決方案
添加表情時(shí)檢查是否聚焦,無(wú)聚焦執(zhí)行fcous強(qiáng)制聚焦
let obj = this.$refs.edit; // obj 是可編輯的div
let range, node;
if (!obj.hasfocus) {
obj.focus();
}
問題2解決方案
window.getSelection:選擇的文本范圍或光標(biāo)的當(dāng)前位置。
getRangeAt
返回選區(qū)開始的節(jié)點(diǎn)(Node)。
因?yàn)橥ǔG闆r下用戶只能選擇一個(gè)范圍,所以只有一個(gè)選區(qū)(range),此方法一般為getRangeAt(0)
range = window.getSelection().getRangeAt(0);
range.collapse(false); //光標(biāo)移至最后
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="請(qǐng)輸入內(nèi)容"
></div>
<ul>
<li v-for="item in emojis" :key="item.name">
<img :src="item.path" alt="" @click="getEmojis" />
</li>
</ul>
<button @click="senMsg">發(fā)送</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:選擇的文本范圍或光標(biāo)的當(dāng)前位置。
getRangeAt
返回選區(qū)開始的節(jié)點(diǎn)(Node)。
因?yàn)橥ǔG闆r下用戶只能選擇一個(gè)范圍,所以只有一個(gè)選區(qū)(range),此方法一般為getRangeAt(0)
*/
if (window.getSelection && window.getSelection().getRangeAt) {
range = window.getSelection().getRangeAt(0);
range.collapse(false); //光標(biāo)移至最后
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>
``

浙公網(wǎng)安備 33010602011771號(hào)