前言

起因是工作中微信小程序要做一个实时聊天功能并且可以发表情,在微信官方文档中其实是有emoji扩展组件的,用法也大概说明;自己看了一下文档。

之后自己理了理思路,摸索着自己写一个。

正文Emoji代码大全:http://www.oicqzone.com/tool/emoji/

以下代码都是使用的上面网址的emoji表情代码

首先分析一下,微信小程序聊天功能还带发表情的思路,我的思路如下

用户在input框输入内容&选择表情,input中显示的是内容和表情(eg:这是内容😵);

前端进行相关转码处理,将input的内容变成(这是内容#emo34_),然后点击发送将转码后的文本内容发送给后端

websocket服务器进行广播,将(这是内容#emo34_)这段文字发给前端

前端对(这是内容#emo34_)这段文字进行解码处理,最后变成(这是内容😵),从而渲染在页面上

直接上代码
准备工作:
//失焦后确认焦点位置
blurcursor: '',
//失焦后的文本
blurText: '',
//用于存储当前表情
currentEmoji: {},
//当前文本内容,用于用户输入框展示
inputText: '',
//当前文本内容,所有表情转换为(#emoxx_)格式,发给后端也是发送inputText_code
inputText_code: '',
//input框光标位置,后续input框删除文字或标签会用到
input_cursor: 0,
//定义一个emoji表情的数组,emoji字段用于展现到页面上,code字段用于传输
emojiArr: [
      { emoji: '😠', code: '#emo30_' },
      { emoji: '😩', code: '#emo31_' },
      { emoji: '😲', code: '#emo32_' },
      { emoji: '😞', code: '#emo33_' },
      { emoji: '😵', code: '#emo34_' },
      { emoji: '😰', code: '#emo35_' },
      { emoji: '😒', code: '#emo36_' },
      { emoji: '😍', code: '#emo37_' },
      { emoji: '😤', code: '#emo38_' },
      { emoji: '😜', code: '#emo39_' },
      { emoji: '😝', code: '#emo40_' },
      { emoji: '😋', code: '#emo41_' },
      { emoji: '😘', code: '#emo42_' },
      { emoji: '😚', code: '#emo43_' },
      { emoji: '😷', code: '#emo44_' },
      { emoji: '😳', code: '#emo45_' },
      { emoji: '😃', code: '#emo46_' },
      { emoji: '😅', code: '#emo47_' },
      { emoji: '😆', code: '#emo48_' },
      { emoji: '😁', code: '#emo49_' },
      { emoji: '😂', code: '#emo50_' },
      { emoji: '😊', code: '#emo51_' },
      { emoji: '😄', code: '#emo53_' },
      { emoji: '😢', code: '#emo54_' },
      { emoji: '😭', code: '#emo55_' },
      { emoji: '😨', code: '#emo56_' },
      { emoji: '😣', code: '#emo57_' },
      { emoji: '😡', code: '#emo58_' },
      { emoji: '😌', code: '#emo59_' },
      { emoji: '😖', code: '#emo60_' },
      { emoji: '😔', code: '#emo61_' },
      { emoji: '😱', code: '#emo62_' },
      { emoji: '😪', code: '#emo63_' },
      { emoji: '😏', code: '#emo64_' },
      { emoji: '😓', code: '#emo65_' },
      { emoji: '😥', code: '#emo66_' },
      { emoji: '😫', code: '#emo67_' },
      { emoji: '😉', code: '#emo68_' },
    ]
实现:
  1. 在wxml中将emojiArr遍历,取emoji字段渲染在页面中,data-code为code字段的内容,data-emoji为emoji字段
<view style="display:{{bottom3Isshow}}" class="bottom-3">
            <view class="emoji" wx:for="{{emojiArr}}" wx:for-item="item" wx:key="id" bindblur='bindblur'  bindtap='changeCurrentEmoji' data-code='{{item.code}}' data-emoji='{{item.emoji}}'>
                {{item.emoji}}
            </view>
</view>
  1. 为上面遍历出来的表情都加一个点击事件,用于存储当前表情
//添加当前表情
  changeCurrentEmoji(e) {
    let tempCursor = this.data.blurcursor
    let tempText = (this.data.blurText).split('')
    if (tempCursor == tempText.length) {
      this.setData({
        currentEmoji: e.currentTarget.dataset
      }, () => {
        this.setData({
          inputText: this.data.inputText + this.data.currentEmoji.emoji
        })
        this.setData({
          inputText_code: this.data.inputText_code + this.data.currentEmoji.code
        })
      })
    } else {
      let inputText_code = this.data.inputText_code
      let inputText = this.data.inputText
      inputText_code = inputText_code.split(/[\#,_]/)
      //去空
      inputText_code = inputText_code.filter(value => value)
      inputText = inputText.split('')
      for (let i = 0; i < inputText_code.length; i++) {
        if (inputText_code[i].indexOf("emo") == -1) {
          inputText_code[i] = inputText_code[i].split('')
        } else {
          inputText_code[i] = ['emo_delet', inputText_code[i]]
        }
      }
      //扁平化
      inputText_code = [].concat.apply([], inputText_code);
      console.log('inputText_code', inputText_code)
      inputText_code.splice(tempCursor, 0, e.currentTarget.dataset.code)
      let newCode = inputText_code.filter(value => {
        if (value.indexOf('emo_delet') == -1) {
          return value
        }
      })
      for (let i = 0; i < newCode.length; i++) {
        if (newCode[i].indexOf("emo") != -1) {
          // console.log(newCode[i])
          let temp = newCode[i].split('')
          if(temp[0] != '#'){
            newCode[i] = "#" + newCode[i] + "_"
          }
        }
      }
      let newCode_1 = [...newCode]
      let newCode_2 = ''
      for(let i=0;i<newCode_1.length;i++){
        newCode_2+=newCode_1[i]
      }

      for (let i = 0; i < newCode.length; i++) {
        if (newCode[i].indexOf("emo") != -1) {
          for (let j = 0; j < this.data.emojiArr.length; j++) {
            if ((this.data.emojiArr[j].code).indexOf(newCode[i]) != -1) {
              newCode[i] = this.data.emojiArr[j].emoji
            }
          }
        }
      }
      let newText = newCode
      let newText_2 = ''
      for(let i=0;i<newText.length;i++){
        newText_2+=newText[i]
      }
      this.setData({
        inputText:newText_2,
        inputText_code:newCode_2
      })
      console.log('newCode', newCode_2)
      console.log('newText', newText_2)
    }
  },

//监听input焦点
  bindblur(e) {
    console.log(e.detail) //cursor
    this.setData({
      blurcursor: e.detail.cursor,
      blurText: e.detail.value
    })
  },
  1. 为input绑定内容改变事件,用于实时改变inputText(用于显示在用户输入的input框)和inputText_code(用于发送给服务器)的值
gitText(e) {
    //获取当前输入和上一次输入的差数
    let temp = (e.detail.value).slice((this.data.inputText).length)
    //确定光标位置,因为涉及到用户输入内容后可能会删除部分内容,这里做了这个处理,定位到用户想删除的内容从而进行改变inputText_code的值
    if (parseInt(e.detail.keyCode) == 8) {
      this.setData({
        input_cursor: e.detail.cursor
      }, () => {
          //这里的transcoder()方法后面会写到
        this.transcoder()
      })
    }
    else {
      this.setData({
        inputText: e.detail.value
      })
      this.setData({
        inputText_code: this.data.inputText_code + temp
      })
    }

  },
//文字带表情转为文字+表情code
  transcoder() {
    let cursor = parseInt(this.data.input_cursor) + 1
    let temp = this.data.inputText_code
    let temp_1 = temp.split(/[\#,_]/)
    //去空字符串
    let newtemp = temp_1.filter(value => value)
    let count = 0
    for (let i = 0; i < newtemp.length; i++) {
      if (newtemp[i].indexOf('emo') == -1) {
        count += newtemp[i].length
        if (count >= cursor) {
          let delIndex = newtemp[i].length - (count - cursor) - 1
          let temp_2 = newtemp[i].split('')
          temp_2[delIndex] = ''
          let temp_3 = temp_2.filter(value => value)
          let str1 = ''
          for (let v = 0; v < temp_3.length; v++) {
            str1 += temp_3[v]
          }
          newtemp[i] = str1
          break
        }
      } else {
        count += 2
        if (count - 1 == cursor) {
          newtemp[i] = ''
        }
      }
    }
    let str = ''
    for (let y = 0; y < newtemp.length; y++) {
      if (newtemp[y].indexOf('emo') != -1) {
        str += "#" + newtemp[y] + "_"
      } else {
        str += newtemp[y]
      }
    }
    this.setData({
      inputText_code: str
    })
  },
  1. 将服务器发送过来的内容,也就是发给服务器的inputText_code转换为文字+表情
//接收带表情的文字把表情转码
  decode(str) {
    let tempArr = str.split(/[\#,_]/)
    let newArr = tempArr.filter(value => value)
    for (let i = 0; i < newArr.length; i++) {
      for (let j = 0; j < (this.data.emojiArr).length; j++) {
        if (newArr[i].indexOf('emo') != -1 && (this.data.emojiArr[j]).code.indexOf(newArr[i]) != -1) {
          newArr[i] = (this.data.emojiArr[j]).emoji
        }
      }
    }
    let src = ''
    for (let i = 0; i < newArr.length; i++) {
      src += newArr[i]
    }
    return src
  },
最后总结一下:

在代码中以数组的方式存储emoji表情,数组的每一个值是一个对象,对象中有emoji表情代码和自定义的表情码,用户在input框输入文字加标签时,显示的是文字+表情代码;实际上传送给后端的是文字+自定义的表情码;当后端有内容传给前端时,前端再对文字+自定义表情码进行转换,最终变成文字+表情代码渲染在页面上。

这样的好处其实是为了减轻后端压力,不必考虑数据库是否可以存放emoji表情代码,数据库中只用存放特定的字符串,发给前端使用时,前端对特定的字符串进行处理