(已解决)Unicode高位码点emoji表情无法解析

问题描述

我在制作游戏论坛项目,希望制作一个表情库,我参考了菜鸟的emoji手册,并且使fromCharCode函数来进行字符串转换,但是经过我的测试,对于超过5位数的高位码点,无法正常解析。

源码:

<template>
  <div class="container">
    <!-- 网页底部的表情按钮 -->
    <el-button
      type="text"
      @click="drawerVisible = true"
    >表情</el-button>

    <!-- 使用Element-UI的抽屉组件 -->
    <el-drawer
      :visible.sync="drawerVisible"
      size="300px"
      position="right"
      :before-close="handleClose"
    >
      <!-- 遍历并显示可点击复制的特殊Unicode字符 -->
      <div
        v-for="(char, index) in specialChars"
        :key="index"
        @click="copyToClipboard(char)"
        class="emoji-item"
      >{{ charDisplay[index] }}</div>
    </el-drawer>

    <!-- 输入框 -->
    <input
      type="text"
      v-model="copiedText"
      ref="input"
    />
  </div>
</template>

<script>
export default {
  data() {
    return {
      drawerVisible: false,
      copiedText: "",
      // 新增数组存储多个Unicode码点表示的表情
      specialChars: [
        String.fromCharCode(8986),
        String.fromCharCode(8987),
        String.fromCharCode(9193),
        String.fromCharCode(9194),
        String.fromCharCode(9195),
        String.fromCharCode(128155),
      ],
      // 将码点转换为显示字符的数组
      charDisplay: [
        String.fromCharCode(8986),
        String.fromCharCode(8987),
        String.fromCharCode(9193),
        String.fromCharCode(9194),
        String.fromCharCode(9195),
        String.fromCharCode(128155),
      ],
    };
  },
  methods: {
    handleClose(done) {
      // 关闭前的处理逻辑(如果需要的话)
      done();
    },

    copyToClipboard(text) {
      this.copiedText = text;
      const input = this.$refs.input;
      input.focus();
      input.select();
      document.execCommand("copy");
      input.blur();
    },
  },
};
</script>

<style>
.emoji-item {
  display: inline-block;
  width: 40px;
  height: 40px;
  text-align: center;
  line-height: 40px;
  margin: 5px;
}
</style>

问题产生原理及解决方法

fromCharCode函数可以把Unicode码点转换成对应的字符,以便在页面上显示表情符号。
但是对于超过 \ufff 的高位码点,就是我说的六位数码点,实际上对于六位数的Unicode已经不说属于码点范围,而是一个 Unicode 字符簇。需要我们用两个UTF-16编码单元,也就是四个十六进制数字来表示,查询资料后,这个被叫做代理对。

解决办法:通过次转换拆分后再进行字符串转换,就可以正常渲染了
 

let highSurrogate = Math.floor((codePoint - 0x10000) / 0x400) + 0xD800;
let lowSurrogate = (codePoint - 0x10000) % 0x400 + 0xDC00;
let emojiCharacter = String.fromCharCode(highSurrogate, lowSurrogate);

例:126980高位编码的红中表情已被正常显示

全部修改后源码:

<template>
  <div class="container">
    <!-- 网页底部的表情按钮 -->
    <el-button
      type="text"
      @click="drawerVisible = true"
    >表情</el-button>

    <!-- 使用Element-UI的抽屉组件 -->
    <el-drawer
      :visible.sync="drawerVisible"
      size="300px"
      position="right"
      :before-close="handleClose"
    >
      <!-- 遍历并显示可点击复制的特殊Unicode字符 -->
      <div
        v-for="(char, index) in renderedSpecialChars"
        :key="index"
        @click="copyToClipboard(char)"
        class="emoji-item"
      >{{ char }}</div>
    </el-drawer>

    <!-- 输入框 -->
    <input
      type="text"
      v-model="copiedText"
      ref="input"
    />
  </div>
</template>

<script>
export default {
  data() {
    const highCodePointsDec = [
      126980, // 示例:笑脸表情
      // 其他高位Unicode码点...
    ];

    // 将十进制Unicode码点转换为可以渲染的字符串
    const specialChars = highCodePointsDec.map((codePoint) => {
      if (codePoint > 0xffff) {
        // 处理高位码点
        const highSurrogate =
          Math.floor((codePoint - 0x10000) / 0x400) + 0xd800;
        const lowSurrogate = ((codePoint - 0x10000) % 0x400) + 0xdc00;
        return String.fromCharCode(highSurrogate, lowSurrogate);
      } else {
        return String.fromCharCode(codePoint); // BMP内直接转换
      }
    });

    return {
      drawerVisible: false,
      copiedText: "",
      renderedSpecialChars: specialChars,
      charDisplay: specialChars, // 在此情况下,由于已经是渲染后字符,无需额外转换
    };
  },
  methods: {
    handleClose(done) {
      // 关闭前的处理逻辑(如果需要的话)
      done();
    },

    copyToClipboard(text) {
      this.copiedText = text;
      const input = this.$refs.input;
      input.focus();
      input.select();
      document.execCommand("copy");
      input.blur();
    },
  },
};
</script>

<style>
.emoji-item {
  display: inline-block;
  width: 40px;
  height: 40px;
  text-align: center;
  line-height: 40px;
  margin: 5px;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值