问题描述
我在制作游戏论坛项目,希望制作一个表情库,我参考了菜鸟的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>