Spaces:
Sleeping
Sleeping
Upload 45 files
Browse files- components/Emoji.tsx +38 -3
components/Emoji.tsx
CHANGED
|
@@ -7,6 +7,14 @@ interface EmojiProps {
|
|
| 7 |
size?: number | string;
|
| 8 |
}
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
// 将 Unicode 转换为 Twemoji 兼容的 Hex 文件名
|
| 11 |
const getEmojiHex = (emoji: string) => {
|
| 12 |
try {
|
|
@@ -19,13 +27,37 @@ const getEmojiHex = (emoji: string) => {
|
|
| 19 |
};
|
| 20 |
|
| 21 |
export const Emoji: React.FC<EmojiProps> = memo(({ symbol, className = '', size }) => {
|
| 22 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
const isEmoji = /\p{Emoji}/u.test(symbol);
|
| 24 |
-
|
| 25 |
if (!isEmoji) {
|
| 26 |
return <span className={className} style={{ fontSize: size }}>{symbol}</span>;
|
| 27 |
}
|
| 28 |
|
|
|
|
| 29 |
const hex = getEmojiHex(symbol);
|
| 30 |
|
| 31 |
// 备用方案:如果转换失败,回退到原生显示
|
|
@@ -43,7 +75,7 @@ export const Emoji: React.FC<EmojiProps> = memo(({ symbol, className = '', size
|
|
| 43 |
style={{
|
| 44 |
width: size || '1em',
|
| 45 |
height: size || '1em',
|
| 46 |
-
verticalAlign: '-0.1em' //
|
| 47 |
}}
|
| 48 |
onError={(e) => {
|
| 49 |
// 加载失败时隐藏图片,显示原生字符
|
|
@@ -51,6 +83,9 @@ export const Emoji: React.FC<EmojiProps> = memo(({ symbol, className = '', size
|
|
| 51 |
const span = document.createElement('span');
|
| 52 |
span.innerText = symbol;
|
| 53 |
span.className = className || '';
|
|
|
|
|
|
|
|
|
|
| 54 |
e.currentTarget.parentElement?.insertBefore(span, e.currentTarget);
|
| 55 |
}}
|
| 56 |
/>
|
|
|
|
| 7 |
size?: number | string;
|
| 8 |
}
|
| 9 |
|
| 10 |
+
// 智能检测:是否为旧版 Windows 系统 (Windows 7, Vista, XP)
|
| 11 |
+
// Windows NT 6.1 = Windows 7
|
| 12 |
+
// Windows NT 6.0 = Vista
|
| 13 |
+
// Windows NT 5.x = XP
|
| 14 |
+
// Windows 8 (6.2) 及以上系统原生支持彩色 Emoji,无需替换
|
| 15 |
+
const isLegacyWindows = typeof navigator !== 'undefined' &&
|
| 16 |
+
/Windows NT (5\.|6\.[0-1])/.test(navigator.userAgent);
|
| 17 |
+
|
| 18 |
// 将 Unicode 转换为 Twemoji 兼容的 Hex 文件名
|
| 19 |
const getEmojiHex = (emoji: string) => {
|
| 20 |
try {
|
|
|
|
| 27 |
};
|
| 28 |
|
| 29 |
export const Emoji: React.FC<EmojiProps> = memo(({ symbol, className = '', size }) => {
|
| 30 |
+
// 【性能优化】如果是现代系统,直接使用原生字体渲染
|
| 31 |
+
// 优点:无网络请求、加载快、与文字对齐更完美
|
| 32 |
+
if (!isLegacyWindows) {
|
| 33 |
+
return (
|
| 34 |
+
<span
|
| 35 |
+
className={className}
|
| 36 |
+
style={{
|
| 37 |
+
fontSize: size,
|
| 38 |
+
// 强制指定 Emoji 字体栈,确保在各平台优先调用彩色字体
|
| 39 |
+
fontFamily: '"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", "Android Emoji", sans-serif',
|
| 40 |
+
lineHeight: '1em',
|
| 41 |
+
verticalAlign: 'middle',
|
| 42 |
+
display: 'inline-block'
|
| 43 |
+
}}
|
| 44 |
+
role="img"
|
| 45 |
+
aria-label={symbol}
|
| 46 |
+
>
|
| 47 |
+
{symbol}
|
| 48 |
+
</span>
|
| 49 |
+
);
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
// --- 以下逻辑仅在 Windows 7 等旧系统执行 ---
|
| 53 |
+
|
| 54 |
+
// 1. 简单正则判断是否包含 Emoji 字符 (避免对纯文本进行昂贵的 Hex 转换)
|
| 55 |
const isEmoji = /\p{Emoji}/u.test(symbol);
|
|
|
|
| 56 |
if (!isEmoji) {
|
| 57 |
return <span className={className} style={{ fontSize: size }}>{symbol}</span>;
|
| 58 |
}
|
| 59 |
|
| 60 |
+
// 2. 转换为图片链接
|
| 61 |
const hex = getEmojiHex(symbol);
|
| 62 |
|
| 63 |
// 备用方案:如果转换失败,回退到原生显示
|
|
|
|
| 75 |
style={{
|
| 76 |
width: size || '1em',
|
| 77 |
height: size || '1em',
|
| 78 |
+
verticalAlign: '-0.1em' // 微调对齐,尽量模拟文字基线
|
| 79 |
}}
|
| 80 |
onError={(e) => {
|
| 81 |
// 加载失败时隐藏图片,显示原生字符
|
|
|
|
| 83 |
const span = document.createElement('span');
|
| 84 |
span.innerText = symbol;
|
| 85 |
span.className = className || '';
|
| 86 |
+
span.style.fontSize = typeof size === 'number' ? `${size}px` : (size as string) || '';
|
| 87 |
+
// 继承字体设置以防万一
|
| 88 |
+
span.style.fontFamily = '"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif';
|
| 89 |
e.currentTarget.parentElement?.insertBefore(span, e.currentTarget);
|
| 90 |
}}
|
| 91 |
/>
|