vue聊天发送Emoji表情

在用web端写聊天发送表情的功能中,使用web端系统自带的unicode表情会出现app跟web表情不统一问题且还不好看,在这里我想到了一个非常好的思路,可以解决这个问题!

那就是发送表情用图片的形式呈现,然后发给后端解析成自定义的编码标识,靠这个标识来回显!

具体的实现思路是这样的,先编辑好json数据:

复制代码
{
    "/::1f600::/": {
        "code": "/::1f600::/",
        "name": "Grinning Face",
        "unified": "1f600",
        "img_url": "/static/images/apple_emoji/1f600.png"
    },
    "/::1f603::/": {
        "code": "/::1f603::/",
        "name": "Smiling Face with Open Mouth",
        "unified": "1f603",
        "img_url": "/static/images/apple_emoji/1f603.png"
    },
    "/::1f604::/": {
        "code": "/::1f604::/",
        "name": "Smiling Face with Open Mouth and Smiling Eyes",
        "unified": "1f604",
        "img_url": "/static/images/apple_emoji/1f604.png"
    },
}

这是一个表情包数据,里面有图片有name还有标识,以键值对的形式,/::1f600::/是相当于一个表情包标识

数据处理好了,接下来最难的是输入框,当我想让表情包在输入框里显示,一般的输入框是不行的,这里用到了div的contenteditable="true"属性,可以让这个div处于编辑模式,跟富文本编辑器一样

先展示html:

html 复制代码
<template>
    <div class="chat-room">
        <div class="input-message mt-5 mb-5 scrollbar" contenteditable="true" @keydown="handleKeyDown" @blur="handleBlur"></div>
    </div>
</template>

表情包用上面那个json数据,展示出来,效果图:

当我点一下这个表情的时候,执行写好的js逻辑,把这个表情图片放到div编辑器里,代码如下:

javascript 复制代码
inputMessage.focus()
var contextMenu = document.createElement('img')//创建一个img元素
contextMenu.setAttribute('data-code', value.code) // 添加自定义属性
contextMenu.className = 'emoji-img'
contextMenu.style.margin = '0 1px'
contextMenu.src = emojiJSON[value.code] ? emojiJSON[value.code].img_url : ''
// 获取当前选区
var selection = window.getSelection()
if (selection.rangeCount > 0) {
    // 获取选区中的第一个范围
    var range = selection.getRangeAt(0)

    // 将范围的起点移到插入节点之前
    range.insertNode(contextMenu)

    // 创建一个新的范围,用于设置光标位置
    var newRange = document.createRange()

    // 将新范围的起点设置为新插入元素之后
    newRange.setStartAfter(contextMenu)
    newRange.collapse(true) // 折叠范围,这样光标就会在新插入元素后

    // 清空当前选区,并添加新的范围
    selection.removeAllRanges()
    selection.addRange(newRange)
}
// 等待 DOM 更新后滚动到底部
nextTick(() => {
    inputMessage.scrollTop = inputMessage.scrollHeight
})

插入表情效果图:

给你们看下dom结构:

表情跟文字的结合:

现在已经实现了文字跟表情在输入框里显示,接下来继续处理输入框里面的逻辑,实现换行编辑,模仿微信的效果:

javascript 复制代码
//文本域键盘事件
const handleKeyDown = (event) => {
    if (event.ctrlKey && event.key === 'Enter') {
        event.preventDefault() // 阻止默认行为
        const div = document.createElement('div') // 创建一个 <div> 元素
        div.setAttribute('data-type', 'br') // 添加自定义属性
        const br = document.createElement('br') // 创建一个 <br> 元素
        div.appendChild(br) // 将 <br> 元素添加到 <div> 中
        const selection = window.getSelection()
        if (selection.rangeCount > 0) {
            const range = selection.getRangeAt(0)
            range.deleteContents() // 删除当前选区内容(如果有)
            range.insertNode(div) // 插入换行符
            range.setStartAfter(div) // 将光标移到换行符之后
            range.collapse(true) // 折叠选区
            selection.removeAllRanges() // 清除所有选区
            selection.addRange(range) // 添加新的选区
        }
        // 等待 DOM 更新后滚动到底部
        nextTick(() => {
            inputMessage.scrollTop = inputMessage.scrollHeight
        })
        return
    }
    if (event.key === 'Enter') {
        // 在这里执行按下Enter时的操作
        event.preventDefault() // 阻止默认的回车换行行为
        sendMessage()
    }
    //回退事件
    if (event.key === 'Enter') {
    }
}
const handleBlur = () => {
    inputMessage.focus()
}

现在实现发送效果,这里是核心代码,在发送之前先处理一下数据,解析成后端可以保存的格式:

javascript 复制代码
const sendMessage = () => {
    const div = document.createElement('div') // 创建一个 <div> 元素
    div.innerHTML = inputMessage.innerHTML
    // 使用正则表达式进行替换
    function replaceNestedDivs(html) {
        // 定义正则表达式以匹配 <div data-type="br">...</div> 标签,并捕获其中的内容和 <img> 标签(如果有的话)
        const regex = /<div\s+data-type="br">(.*?)(<img[^>]*>)?/gi

        // 使用替换函数对匹配到的内容进行处理
        return html.replace(regex, function (match, p1, p2) {
            const content = p1 ? p1.trim() : '' // 去除捕获文本内容的前后空格
            const imgTag = p2 ? ` ${p2}` : '' // 如果捕获的 <img> 标签存在,则返回空格加上该标签
            // 返回处理后的内容和 <img> 标签
            return `\n${content}${imgTag}`
        })
    }
    div.innerHTML = replaceNestedDivs(div.innerHTML)
    div.innerHTML = div.innerHTML.replace(/<img\s+data-code="([^"]+)"[^>]*>/g, '$1')
    state.message = div.innerText
    // 如果文本内容不为空,则执行提交操作
    if (state.message) {
        state.messageList.push({
            id: 2,
            content: initMessage(state.message),
            time: '2024-6-5 15:41',
            type: 'system_user',
            avatar: 'https://www.wenpblog.com/cdn/static/header/2812.png',
        })
        state.message = ''
        inputMessage.innerHTML = ''
        initScroll()
    } else {
        if (state.sendTipTimeout) {
            return
        }
        state.sendTip = true
        state.message = ''
        inputMessage.innerHTML = ''
        state.sendTipTimeout = setTimeout(() => {
            state.sendTip = false
            state.sendTipTimeout = null
        }, 1500)
    }
}

//处理聊天数据
const initMessage = (text) => {
    // 定义一个函数来将特定格式的字符串替换为图片
    function replaceWithEmojiImages(text) {
        // 定义一个正则表达式来匹配 /::1f600::/ 格式的字符串
        const regex = /\/::(.*?)::\//g

        // 使用 replace 方法和回调函数进行替换
        return text.replace(regex, (match, p1) => {
            // 构建图片标签
            const imgSrc = `${emojiJSON[match] ? emojiJSON[match].img_url : ''}` // 假设图片存储在这个路径
            return `<img data-code="${match}" src="${imgSrc}" class="emoji-img"/>`
        })
    }

    // 调用函数进行替换
    const outputText = replaceWithEmojiImages(text)
    return outputText
}

发送回显的样式:

这是处理后传给后端的消息数据:

复制代码
/::1f600:://::1f602::/啊是大飒飒的啊是大/::1f60d::/,太搞笑; /::1f602::/

注意:回显消息的时候,需要用v-html去显示

最终效果图:

相关推荐
10年前端老司机2 小时前
什么!纯前端也能识别图片中的文案、还支持100多个国家的语言
前端·javascript·vue.js
摸鱼仙人~2 小时前
React 性能优化实战指南:从理论到实践的完整攻略
前端·react.js·性能优化
程序员阿超的博客3 小时前
React动态渲染:如何用map循环渲染一个列表(List)
前端·react.js·前端框架
magic 2453 小时前
模拟 AJAX 提交 form 表单及请求头设置详解
前端·javascript·ajax
小小小小宇7 小时前
前端 Service Worker
前端
只喜欢赚钱的棉花没有糖8 小时前
http的缓存问题
前端·javascript·http
小小小小宇8 小时前
请求竞态问题统一封装
前端
loriloy8 小时前
前端资源帖
前端
源码超级联盟8 小时前
display的block和inline-block有什么区别
前端
GISer_Jing8 小时前
前端构建工具(Webpack\Vite\esbuild\Rspack)拆包能力深度解析
前端·webpack·node.js