表情符号(Emoji)在Web中的使用

发布于 大漠

大约从 1998 年(还是 1999)年开始有了 Emojis(表情符号) 之后,Emojis 就风靡一时,在一些聊天工具和Web应用上也受到广泛的使用。也就是说,它不再局限于用在聊天工具用来交流,还在 Web 页面或应用上使用也越来越多,在一些组件上的使用也更频繁。

就在最近,微软发布了全新的 3D 风格的表情符号(Emoji),这套 3D 风格的表情符号已经在 Flipgrid 上线,并将于今年晚些时候登录 Teams 和 Windows 系统,Yammer、Outlook 和其他平台将在明年推出:

对于日常用户来说,表情符号是很好的。它们很有趣,也很容易使用。对于Web开发者而言,想在 HTML、CSS 和 JavaScript 中使用表情符号,情况还是有点不同。在这篇文章中,我们就和大家一起探讨表情符号在 Web 上的使用。

Emoji的历史

在开始聊 Emoji 在 Web 中的使用之前,先简单地了解 Emoji的历史。下面这部分内容摘自 维基百科对 Eemoji 的介绍:

第一个 Emoji 是由 栗田穰崇 于1998年或1999年创造,他当于隶属于 NTT DoCoMo 公司 i-mode 移动互联平台的团队。第一套 Emoji 包括 172 个 12 x 12 像素的图标,设计的初衷是作为 i-mode 消息功能的一部分帮助促进电子通讯,并作为区别于其他业务的特色功能。然后在 1997 年, Nicolas Loufrani 注意到, ASCII 表情符号在移动技术中的使用正在增加,他开始尝试动画效果的笑脸表情,目的是使用纯标点符号设计一套与现有 ASCII 表情对应的彩色图标,以促进其在数位领域的使用。 Loufrani 由此创造了第一套图形化表情、并编译了在线表情符号词典,将这些符号分成不同类别,包括:经典类、情绪类、旗标类、庆贺类、娱乐类、体育类、天气类、动物类、饮食类、民族类、职业类、行星类、星座类、婴儿类等,这些设计最初于 1997 年在美国版权局注册,随后全套图标于 1998 年以 .gif 格式文件在网络上发布,成为科技行业中使用的第一套图形化表情符号:

2000年,Loufrani 创建的表情目录开始提供下载,用户开始可以从互联网上为手机下载 Loufrani 创建的表情目录,该目录编译了超过 1000 个笑脸表情符号入其 ASCII 版本。该目录在 2002 年由 Marabout 以《Dico Smileys》书籍形式出版本。2021年,表情符号公司 Smiley Company 开始向各家电信公司的手机提供 Loufrani 图形表情符号的授权下载,这些公司包括诺基亚、摩托罗拉、三星等。

如果你对 Emojis 历史感兴趣的话,还可以阅读:

究竟什么是表情符号

我们所知道的表情符号是一个个彩色图标。但其更准确的定义是:

表情符号是使用在网页或聊天中的形意符号

大多数情况之下,表情符号给我们的印象就是 它们是传统意义上的图像(或图标),但它们并不是。它们更像是 字母数字标点符号奇怪的符号 ,我们更倾向于把它们当作 文本 来处理。

现在,我们只需要知道表情符号是:

  • 只是字符(字符串)
  • 可以被选择,复制、粘贴,并且调整大小等
  • 有一个更原始的数字表示,可以用相应的数字来描述表情符号

初步对表情符号有这些认识就OK了。现在我们开始看看表情符号在 Web 中的使用和所起的作用。

HTML 中的表情符号

要在 HTML 文档中使用表情符号,我们首要做的第一件事情就是把 HTML 文档的字符Unicode编码设置为 UTF-8。这样做可以确保表情符号在你的用户可能使用的浏览器和设备上(客户终端)显示一致。做到这一点很简单,只需要在 <head> 中使用 <meta> 来指定字符Unicode编码:

<meta charset="UTF-8">

有了这个基础保障之后,我们可以通过下面两种方式把表情符号添加到 HTML 文档中:

  • 在 HTML 中直接使用表情符号
  • 在 HTML 中使用表情符号对应的原始Unicode码编码

先来看 在 HTML 中直接使用表情符号。

在 HTML 中直接使用表情符号

在 HTML 中直接使用表情符号其实很简单,可以从某个地方复制粘贴到 HTML 文档中。不过,我们需要一个应用或网站,允许你复制表情符号的原始字符形式。现在在互联网上这样的网站或应用很多,你在 Google 搜索框中输入 Emojis 的关键词可以搜索一大堆,比如:

我个人比较喜欢 Emojipedia ,可以在这个网站上直接搜索或浏览你想要找的任何表情符号。一旦找到了你要的表情符号,你就可以看到并复制该表情符号:

一旦你复制了它,只需要在你的标记中(HTML标签元素中)把它粘贴到它的预定目的地,即 把复制过来的表情符号当作 HTML 标签元素的内容(文本节点)

因为表情符号被视为 基于文本的内容,因此你可以在任何支持文本的地方粘贴它们。正如上图所示,你在浏览器中预览你的 HTML 文档时,一切都会正常。如果你使用浏览器开发者工具审核带有表情符号的 HTML也能正常显式:

嗯!就是这么简单!从另一个地方,复制粘贴过来就可以OK!

在 HTML 中使用表情符号对应的Unicode编码

如果有的环境之下直接复制粘贴过来的表情符号放置在 HTML 的标签元素中,浏览器客户端无法正常显示(即直接指定的表情符号不起作用)。我们可以换过一种方式来将表情符号放置到 HTML 的标签元素中。即 使用代表表情符号的Unicode编码,因为每一个表情符号都有自己对应的一个Unicode编码。如果你在使用 Emojipedia 获取表情符号,那么可以看到表情符号对应的Unicode编码(Emojipedia的 “Codepoints”):

或者点击上图红色框的链接部分,可以看到该表情符号更详细的信息:

对于“Girl”表情符号,其对应的Unicode编码是 U+1F467 。如果我们要在 HTML 中使用 U+1F467 对应的表情符号,需要对该Unicode编码做一些转换,使用 &#x 来替代 U+1F467 编码中的 U+ ,即 U+1F467 更换成 &#x1F467 。我们只需要把这个代码(&#x1F467)放到 HTML 标签元素中即可:

<h1>&#x1F467 Girl</h1>

当你使用浏览器浏览的时候,可以看到“Girl”表情符号正常显示出来。最终的效果是一样的,但相比之下,使用表情符号编码看起来很怪,甚至不知道代表是什么:

效果如下:

有的时候你可能在一些网站或应用上获取的表情符号,但没有对应的Unicode编码,那你就无法像上面这样在HTML中使用表情符号。不过我们可以借助 JavaScript 脚本来把表情符号的Unicode编码转译出来:

function emojiUnicode (emoji) {
    var comp;
    if (emoji.length === 1) {
        comp = emoji.charCodeAt(0);
    }
    comp = (
        (emoji.charCodeAt(0) - 0xD800) * 0x400
        + (emoji.charCodeAt(1) - 0xDC00) + 0x10000
    );
    if (comp < 0) {
        comp = emoji.charCodeAt(0);
    }
    return comp.toString("16");
};

或者使用一些在线的转译工具:

有关于这方面更详细的介绍,可以阅读 Dmitri Pavlutin(@panzerdp) 的《What every JavaScript developer should know about Unicode》一文。

CSS 中的表情符号

在 CSS 中使用表情符号和在 HTML 中非常的相似,也可以直接使用表情符号或代表表情符号的Unicode编码。但不同的是,CSS 中使用表情符号是需要具备一定依赖条件的。众所周知,CSS 一般是用来设置元素样式的,但有一个特殊点,使用 content 也可以用来生成内容

熟悉 CSS 的同学可能知道,在 CSS 中可以在伪元素::before::after标记伪元素 ::marker 中使用 content 来生成内容,比如:

h1::before {
    content:"我是一位";
    color: #90f;
}

h1::after {
    content: "(^_^)!";
    color: #f90;
}

h1::marker {
    content: "#01:";
    color: #09f;
}

我们可以把::before::after::markercontent的字符串文本直接替换成表情符号,比如:

除此之外,在 CSS 的content中也可以像 HTML 中一样直接使用表情符号Unicode编码(Codepoint)。和 HTML 不同的是,表情符号Unicode编码中的 U+替换成#&x,而在 CSS 中将 U+ 替换成 \0 。比如花的Unicode编码是 U+1F33A,那么在 CSS 中我们可以用 \01F33A 来替代:

最终的效果如下:

除了我们熟悉的content 生成内容可以使用表情符号之外,在 CSS 的 @counter-style 中的 symbols 中也可以使用表情符号用来设置列表的标记:

来看一个真实的示例:

效果如下:

JavaScript 中的表情符号

在 JavaScript 中使用表情符号和HTML或CSS中使用表情符号是相同的。在 JavaScript 中我们可以动态创建 DOM,可以动态改变 DOM的内容,也可以动态修改元素伪元素的content的值。比如我们在使用.innerText.innerHTML.textContent动态创建内容(或元素)时,可以直接使用表情符号:

也可以把表情符号的Unicode编码当作String.fromCodePoint()方法参数传进来。和 HTML、CSS 类似,在String.fromCodePoint()中参数的Unicode编码需要把 U+ 替换成 0x,比如 U+1F970 替换成 0x1F970

divElement.textContent = String.fromCodePoint(0x1F970);

在JavaScript 中使用表情符号能做更多的事情,比如你有一个表情符号的数组,使用 JavaScript 就可以动态的生成这些表情符号,并插入到 Web中:

const ulElement = document.querySelector("ul");

let emojis = [
    0x1f600, 0x1f604, 0x1f34a, 0x1f344, 0x1f37f, 0x1f363,
    0x1f370, 0x1f355, 0x1f354, 0x1f35f, 0x1f6c0, 0x1f48e,
    0x1f5fa, 0x23f0,  0x1f579, 0x1f4da, 0x1f431, 0x1f42a,
    0x1f439, 0x1f424, 0x1f493, 0x1f49e, 0x1f970, 0x1f929
];

emojis.forEach((emoji) => {
    const liElement = document.createElement("li");
    liElement.textContent = String.fromCodePoint(emoji);
    ulElement.append(liElement);
});

效果如下:

表情符号能用来做些什么?

前面我们花了一些时间了解了表情符号怎么运用于 Web 中(分别在 HTML、CSS 和 JavaScript)。那表情符号能用来做些什么呢?我将通过一些简单的示例来告诉大家,表情符号在 Web 中的基本使用。

列表标记

表情符号最常用的地方就是给列表选项或displaylist-item元素设置列表项标记,可以在li::before::after::markercontent 中使用表情符号。比如:

另外在 @counter-stylesymbols 也可以使用表情符号,来设置列表项标记:

表情符号切换器

@NatalyaCodepen 上使用 input[type="checkbox"]labelfor属性构建一个纯CSS的切换器(Switch),更有意思的是,在input的选中状态和非选状态显示不同的表情符号:

<!-- HTML -->
<!-- 表情符号切换器组件的容器,可以放置在任何地方,子元素都是平级 -->
<div class="emoji-toggle emoji-travel">

    <!-- input选择框在最前面,然后使用 ~ 选择器,可以选择它之后的兄弟元素 -->
    <input type="checkbox" id="toggle2" class="toggle">

    <!-- 表情符运用在 emoji 的伪元素上 -->
    <div class="emoji"></div>

    <!-- 绝对定位,放置在切换器所有元素最上面 -->
    <label for="toggle2" class="well"></label>

</div>

/* CSS */
.emoji-toggle {
    position: relative;
}

.well {
    cursor: pointer;
}

.toggle {
    appearance: none;
    background: transparent;
    position: absolute;
    width: 100%;
    height: 100%;
    cursor: pointer;
    z-index: 100; 
}

/* 选择框未选中状态:off */
.toggle ~.emoji:before { 
    content: "未选中状态表情符号";
    position: absolute;
    left: 0;
    top: -15px;
    font-size: 40px;
    z-index: 1;
    transition: 0.2s;
}

/* 选择框选中状态: on */
.toggle:checked ~.emoji:before {
    content: "选中状态表情符号";
    left: 100%;
    margin-left: -1em;
}

表情符号在滑块上的使用

上面示例是表情符号和input[type="checkbox"]以及label结合在一起构建的切换器的效果。除此之外,我们还可以把表情符号和 input[type="range"] 结合起来使用。

比如,在不同的值显示不同的表情符号(类似一个评分系统),@Bennett Feely 的 Emoji Rating 就是这样的效果:

除了具体的值对应一个表情符号之外,还可以在一个范围内对应一个表情符号,比如 @George W. Park 构建的一个表情符号和滑块结合在一起的效果,会根据温度值的变化来更换表情符号:

你甚至可以结合 input[type="range"]:in-range:out-of-range 根据 minmax 的范围值来设置不同的表情符号,除了input[type="range"] 之外,这种方式也以运用于input[type="number"] 上面。感兴趣的同学不妨一试。

Emoji Bar

我们甚至还可以制作表情符号的Bar,比如 @Steven Lei@Ben Brookes 在 Codepen提供的两个Demo:

心形点赞动效

在《重新创建Twitter点赞动效》 向大家介绍了使用表情符号替代 CSS Sprites实现点赞动效。我们来看看怎么用表情符号实现这样的效果:

HTML结构很简单,一个<input type="checkbox">labelinputidlabelfor值),并且有一个额外的标签元素,比如示例的<i>,同时用data-icon设置表情符号。会使用相关的伪元素来实现一些UI元素效果

其中第三层和第四层都是使用径向渐变来绘制的:

.react:after,
.react i:after{
    content:"";

    --c1:radial-gradient(red    50%,#0000 60%);
    --c2:radial-gradient(orange 50%,#0000 60%);
    background:
        var(--c1),var(--c1),var(--c1),var(--c1),
        var(--c2),var(--c2),var(--c2),var(--c2); 
    background-size:calc(var(--r)/6) calc(var(--r)/6); 
    background-position:
        calc(50% - var(--r)/2) calc(50% - var(--r)/2),
        calc(50% + var(--r)/2) calc(50% - var(--r)/2),
        calc(50% - var(--r)/2) calc(50% + var(--r)/2),
        calc(50% + var(--r)/2) calc(50% + var(--r)/2),
        calc(50% +  0px) calc(50% + var(--r)*0.707),
        calc(50% + var(--r)*0.707) calc(50% +  0px),
        calc(50% - var(--r)*0.707) calc(50% +  0px),
        calc(50% +  0px) calc(50% - var(--r)*0.707);
    background-repeat:no-repeat;
    transform:scale(0);
}

.react i:after {
    background-size:calc(var(--r)/8) calc(var(--r)/8);
    transform:rotate(60deg) scale(0);
}

每一层有八个圆点(四个红色和四个橙色)组成一个圆形。这里运用了一点数学知识,即sin()cos()N*45deg的值。并且使用transition设置过渡效果。

.react i {
    transition: transform 0.6s cubic-bezier(0.5, -12, 1, -12);
}

.react:hover {
    transition: transform 0.25s cubic-bezier(0.5, 400, 0.5, -400);
}

input:checked + .react i {
    transition: filter 0.5s 0.5s, transform 1s cubic-bezier(0, 26.67, 0.5, 26.67);
}

input:checked + .react:before {
    transition: transform 0.5s, border-width 0.5s 0.5s;
}

input:checked + .react:after,
input:checked + .react i:after {
    transition: transform 0.5s 0.5s, opacity 0.4s 0.9s, background-size 0.5s 0.9s;
}

示例中用了cubic-bezier()函数来设置transition-timing-function,如果你对这方面感兴趣的话,可以阅读 @Temani Afif 的《Advanced CSS Animation Using cubic-bezier()》一文。

最终组合出来的效果如下:

基于上面的示例,我们只需要改变 HTML中<i>data-icon可以快速复制出很多效果:

而且我们还可以改变自定义属性的值,可以更换更多不同的颜色:

这样就可以让表情符号背景和每个点的颜色都不一样:

最终效果如下:

注意,上面Demo中表情符号变成灰色,使用的是CSS滤镜的特性filter: grayscale(0);,另外整个布局采用的是Grid布局。如果你对 CSS Grid布局感兴趣的话,可以阅读 图解CSS 系列 中的 CSS Grid 布局系列

卡牌游戏

上面看到的大部分是 CSS 中使用表情符号的示例。我们来看一个JavaScript 对表情符号数组的处理的示例。这个示例来自 @kirupa源码来可以在Github上阅读。这是一个 Koncentration 游戏,每张卡片上绑定了一个表情符号,具体的效果如下:

表情符号做鼠标形状

在 CSS 中我们可以使用cursor属性来指定鼠标形状:

cursorurl()我们除了可以指定鼠标形状的图片之外,还可以使用 SVG 作为 URI,并且还可以结合表情符号,比如:

效果如下:

注意,随着<svg>中的<text>中表情符号数量增加,需要随着调整width的值。

使用 CSS 自定义属性构建表情符号定制器

Jokob Eriksen 在 Codepen 上写了一个很有趣的示例,用 CSS 自定义属性构建了 一个表情符号的定制器。可以选择肤色,头发这样的修改器来调整 表情符号。

核心代码:

#sa:checked ~ .emoji {--skin: '\1F3FB'; }
#sb:checked ~ .emoji {--skin: '\1F3FC'; }
#sc:checked ~ .emoji {--skin: '\1F3FD'; }
#sd:checked ~ .emoji {--skin: '\1F3FE'; }
#se:checked ~ .emoji {--skin: '\1F3FF'; }

#ha:checked ~ .emoji {--hair: '\1F9B0'; }
#hb:checked ~ .emoji {--hair: '\1F9B1'; }
#hc:checked ~ .emoji {--hair: '\1F9B2'; }
#hd:checked ~ .emoji {--hair: '\1F9B3'; }
#he:checked ~ .emoji {--hair: '\1F9B4'; }


.emoji.a:before {
    content: '\1F469' var(--skin,'') '\200D' var(--hair,'');
}

.emoji.b:before {
    content: '\1F468' var(--skin,'') '\200D' var(--hair,'');
}

最终效果如下:

另外,在@keyframes中改变自定义属性的值,还可以制作类似下面这样的动画效果,每s中显示不同的表情符号:

表情符号和可访问性

表情符号在 Web页面或应用上的使用越来越广泛,她可以帮助我们非常容易地沟通复杂的想法,也可以很好的让我们在一些即时通讯上表达自己的感情(或心情)。前面花了一些篇幅和大家探讨了如何在 Web 中使用表情符号,那话又说回来,对于很多用户他可以很好的理解表情符号,但我在想,屏幕阅读器是否可以很好识别出表情符号。为什么有这么一问呢?

那是因为,表情符号是一种表意文字(代表一种想法或概念的图片)。它与以前我们熟悉的图片(比如,.png.gif.jpg等)不同,表情符号是用 Unicode 字符显示的。在 Web 上,这通常是使用 Unicode 字符的十六进制或十进制表示,比如:

<span>&#x1F493;</span>

我们在浏览器中可以看得出来,它是一个心形表情符号。那要是在屏幕阅读器上是什么呢?不妨一试:

亲测在 iOS 的 Voiceover 会朗读出“跳动的爱心”。

我在这个基础上,继续添加了几个其他的表情符号:

Voiceover的效果如下:

注意,这两个示例都是使用了非语义的span标签,如果把标签更换成语义化的HTML标签呢?

和我预期的效果是一致的。但需要提出的是,A11Y也类似浏览器,在一些辅助技术上也是有兼容性的,而且表情符号是也是一种概念图片,如果希望表情符号在所有终端(辅助技术)上有一个较好的效果,可以考虑使用 ARIA 相关的属性,类似模拟<img>的使用。比如在标签上使用 role的值为img并且使用aria-label来描述表情符号,也可以使用aria-labelledbyaria-describedby等特性

<span>&#x1F493;</span>
<span role="img" aria-label="跳动的爱心">&#x1F493;</span>

Web可访问性(A11Y)也是一门非常复杂的技术体系,而且表情符号在可访问性方面也是较为复杂的,如果你对这方面感兴趣的话,可以阅读:

如果你想探知Web可访问性(A11Y)更多的知识,可以阅读小站有关于 A11Y 系列的文章

小结

在这篇文章主要和大家探讨的是表情符号在Web中的使用方式(HTML、CSS 和 JavaScript)以及使用场景。但表情符号是一种表意文本,他在不同的国度,不同的文化背景,不同的人群,不同的时候,即使是同一个表情符号所代表的意思也将会不一样。甚至一不小心,还会造成歧义,引起法律纠纷,所以在使用的时候还是要注意这些文化背景带来的不确定性。

如果我们抛开这些语言和环境以及文化相关的背景,只从技术成面来说的话,表情符号在Web中的使用还是较为简单的。事实上,在一些社交性的Web网站或应用,已经到处可见了。最后附上Socialmediapsychology.eu上的一张图,来描述"为什么我们喜欢表情符号?"来结束有关于表情符号相关的探讨:

最后希望大家喜欢这篇文章。