Unicode 表情符号的颜色

Color for Unicode Emoji

在现代浏览器中可以包含 Emoji 字符,但如何使其成为单一颜色并选择该颜色?

例如,这里有一些表情符号和一些常规(平面 0)Unicode 符号。都应该是红色的,但只有符号呈现为红色。

关联HTML + CSS

<p>
  
</p>
<p>
  ♥★ℹ
</div>

p {
  font-size: 3em;
  color: red
}

由于表情符号非常新,因此尚不支持其样式。

解决方法是使用表情符号字体,例如 Twitter's Twemoji。然后它的样式可以与 Font Awesome 或本机 Unicode 的样式大致相同。

是的,你可以给它们上色!

div {
  color: transparent;  
  text-shadow: 0 0 0 red;
}
<div></div>

您可以用纯色填充它们:

p {
  font-size: 20px;
  color: transparent;
  text-shadow: 0 0 0 blue;
}
<p></p>
<p></p>
<p></p>

或者您可以概述它们:

body {
  background: #fff;
}

p {
  margin: 0;
  color: transparent;
  text-shadow: 0 0 3px blue;
  font-size: 20px;
  position: relative;
}

p::before {
  content: attr(title);
  position: absolute;
  text-shadow: 0 0 0 #fff;
}
<p title=""></p>
<p title=""></p>
<p title=""></p>

并非每个表情符号的作用都相同。有些是旧的文本符号,现在具有(可选或默认)彩色表示,其他的则明确(仅)作为表情符号。这意味着,一些 Unicode 代码点应该有两种可能的表示形式,textemoji。作者和用户应该能够表达他们对其中一个或另一个的偏好。这是目前完成的,否则不可见 variation selectors U+FE0E (text, VS-15) and U+FE0F (emoji, VS-16), but higher-level solutions (e.g. for CSS) 已被提议。

文本样式的表情符号是单色的,应该以前景色显示,即 CSS 中的 currentcolor,就像任何其他字形一样。 Unicode 联盟提供 overview of emojis by style (beta version)。您应该能够将 HTML 中的 &#xFE0E; 附加到 select 文本变体,其中包含标有“默认文本样式”的列中的任何内容; 有 VSs”和“默认表情符号样式; 有VS”。不过,这不包括示例表情符号和许多其他表情符号。

p {
  color: red; font-size: 3em; margin: 0;
  text-transform: text;           /* proposed */
  font-variant-emoji: text;       /* proposed */
  font-variant-color: monochrome; /* proposed */
  font-color: monochrome;         /* proposed */
  font-palette: dark;             /* drafted for CSS Fonts Level 4 */
}
p.hack {
  color: rgba(100%, 0%, 0%, 0);
  text-shadow: 0 0 0 red;
}
p.font {
  font-family: Emojione, Noto, Twemoji, Symbola;
}
@font-face { /* http://emojione.com/developers/ */
  font-family: Emojione;
  src: local("EmojiOne BW"), local("EmojiOne"), local("Emoji One"), 
       /*   https://cdn.rawgit.com/Ranks/emojione/master/assets/fonts/emojione-bw.otf – monochrome only, deprecated, removed
            https://cdn.rawgit.com/Ranks/emojione/master/assets/fonts/emojione-android.ttf – with hack
            https://cdn.rawgit.com/Ranks/emojione/master/assets/fonts/emojione-apple.ttf – with hack */
       url("https://cdn.rawgit.com/Ranks/emojione/master/assets/fonts/emojione-svg.woff2") format("woff2"),
       url("https://cdn.rawgit.com/Ranks/emojione/master/assets/fonts/emojione-svg.woff") format("woff"),
       url("https://cdn.rawgit.com/Ranks/emojione/master/assets/fonts/emojione-svg.otf") format("truetype");
}
@font-face { /* https://www.google.com/get/noto/#noto-emoji-zsye */
  font-family: Noto;
  src: local("Noto Emoji"), local("Noto Color Emoji"), local("Noto"), 
       url("https://cdn.rawgit.com/googlei18n/noto-emoji/master/fonts/NotoEmoji-Regular.ttf");
}
@font-face { /* https://github.com/eosrei/twemoji-color-font/releases */
  font-family: Twemoji;
  src: local("Twemoji");
}
@font-face { /* http://users.teilar.gr/~g1951d/ */
  font-family: Symbola;
  src: local("Symbola");
}
<p title="♥★ℹ without variation selectors">&#x1F418; &#x1F427; &#x1F43C; &#x2665; &#x2605; &#x2139; &#x1F480; &#x1F44C;</p>
<p title="♥★ℹ with text variation selector 15">&#x1F418;&#xFE0E; &#x1F427;&#xFE0E; &#x1F43C;&#xFE0E; &#x2665;&#xFE0E; &#x2605;&#xFE0E; &#x2139;&#xFE0E; &#x1F480;&#xFE0E; &#x1F44C;&#xFE0E;</p>
<p title="♥★ℹ with emoji variation selector 16">&#x1F418;&#xFE0F; &#x1F427;&#xFE0F; &#x1F43C;&#xFE0F; &#x2665;&#xFE0F; &#x2605;&#xFE0F; &#x2139;&#xFE0F; &#x1F480;&#xFE0F; &#x1F44C;&#xFE0F;</p>
<p title="♥★ℹ with `text-shadow` hack" class="hack">&#x1F418; &#x1F427; &#x1F43C; &#x2665; &#x2605; &#x2139; &#x1F480; &#x1F44C;</p>
<p title="♥★ℹ with custom font" class="font">&#x1F418; &#x1F427; &#x1F43C; &#x2665; &#x2605; &#x2139; &#x1F480; &#x1F44C;</p>

我添加了 U+1F480 Skull 和 U+1F44C OK Hand Sign,因为背景应该通过他们的“眼睛”显示出来,我使用数字字符引用只是为了让代码更明显,更健壮,防止复制- 并粘贴错误。

然而,有人提议,两种变体 selector 都可以应用于任何字符,这在大多数情况下都没有效果。请注意,一些供应商,尤其是三星,已经发货(默认)emoji glyphs for several other charactersgoo.gl/a4yK6pgoo.gl/DqtHcc)。

一些(但不是全部)代码点可以文本形式(非基于图片的字形)或表情符号形式(基于图片的字形)绘制。 Unicode 描述可以使用两个变体选择器之一来选择这两种形式:U+FE0E (VARIATION SELECTOR-15) 或 U+FE0F (VARIATION SELECTOR-16)。当以非图片形式绘制时,color CSS 属性 应适用。

示例:

U+2603(雪人)是这样画的:☃

码点序列U+2603 U+FE0E这样画:☃︎

码点序列U+2603 U+FE0F是这样画的:☃️

可以在 http://unicode.org/emoji/charts/emoji-variants.html

中找到更多信息以及参与这些变体序列的代码点的完整列表

(请注意,不同的操作系统在使用裸代码点时可能会选择不同的默认值。例如,尝试在 macOS 中查看此 post 和 iOS - 上面的裸代码点看起来不同!)

如果您需要使用 CSS 之后,您可以像这样处理之前的伪元素

<span class="react-thumb-fly" title="Aimé">
   <button class="react-toggle-icon-thumb"></button>
</span>

然后为 CSS

写这个
.react-toggle-icon-thumb {
  width: 20pt;
  height: 20pt;
  font-size: 30pt;
  position: relative;
  color: gray;
  cursor: pointer;
  border: none;
  background: transparent;
  margin-left: 0px;
  margin-right: 8px;
  top: -0px;
}
.react-toggle-icon-thumb:before, .react-toggle-icon-thumb:after {
  position: absolute;
  top: 0;
  left: 0;
  transition: all .3s ease-out;
  content: "";
  font-family: fontawesome;
  color: transparent;  
 text-shadow: 0 0 0 #3498db;
}

.react-toggle-icon-thumb:hover:before {
  transform: scale(1.2);
  animation: thumbs-up 2s linear infinite;
}

@keyframes thumbs-up {
 25% {
  transform: rotate(20deg);
 }
 50%, 100% {
   transform: rotate(5deg);
 }
}

我想自己做这个,所以我最近想出了一个解决方案,使用更新的 CSS 效果,也适用于 Firefox 和 Edge。

使用滤镜,首先使用深褐色 (1) 对颜色进行标准化,然后将其浸透以获得纯红色。如果你想去掉黑线,在使用 contrast(0) 应用其他滤镜之前从表情符号中吸取对比度。之后,您只需使用 hue-rotate() 将色轮从红色旋转到您想要的任何颜色。请注意,因为我使用的是十进制值而不是 %,所以值 100 表示 10000%。

色调偏移定义为从红色开始。我们很幸运,棕褐色大部分是红色的,所以轮子完美地从 0 度开始。您可以使用 RGB 到 HSL 转换器计算您想要的偏移量。我在这里找到了一篇用 Javascript 写的不错的文章:https://web.archive.org/web/20180808220922/http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c

您必须将色调值乘以 360 才能从该函数获得所需的结果。一个例子是 rgbToHsl(0,100,100)[0]*360。这将 return 180。由于没有红色,这将是预期的结果,您将旋转 180 度远离红色。

正如 Litherium an Crissov 指出的那样,也有文字表情符号。这些在转换时效果更好,而且通常看起来更好。但是,您不能应用我上面描述的方法,除非您首先在文本表情符号上应用 invert(.5),这是因为函数需要某种阴影才能运行。因此,只需将 invert(.5) 添加到每个公式的开头,就可以让它们在所有浏览器上对两个代码点进行操作。

.a { /* Normalize colour to a primary red */
    filter: sepia(1) saturate(100);
}
.b { /* Less saturation so more features, shifted colour 90-degrees */
    filter: sepia(1) saturate(5)  hue-rotate(90deg);
}
.c { /* Remove black outlines if desired by removing contrast */
    filter: contrast(0) sepia(1) saturate(100) hue-rotate(180deg);
}
.d { /* Other shades possible by lowering brightness */
    filter: contrast(0) sepia(1) saturate(100) brightness(.05) hue-rotate(180deg);
}
.e { /* Weird possibilities with no colour normalization */
    filter: contrast(100) hue-rotate(180deg);
}
.ma { /* Invert to provide a gray shade to apply sepia*/
    filter: invert(.5) sepia(1) saturate(100);
}
div {
    font-size: 25px;
}
.emo > div::after {
    content: "⛄";
}
.mono > div::after {
    content: "F30D\FE0EC4\FE0EF60D\FE0EF431\FE0E";
}
<div class="emo">
    <div>-:</div>
    <div class="a">a:</div>
    <div class="b">b:</div>
    <div class="c">c:</div>
    <div class="d">d:</div>
    <div class="e">e:</div>
</div>
<span>Change the code point to text mode:</span>
<div class="mono">
    <div class="a">a:</div>
    <div class="ma">ma:</div>
</div>

更新: 现在 npm 上有更新的软件包可以计算色调等。您不需要在我之前链接的网站上使用该片段。这是一个示例:

如果您想在表情符号中保留详细信息,可以使用 filter: url(svg) where url() will take an svg filter

svg {
  display: none;
}

div {
  -webkit-filter: url(#red);
  filter: url(#red);
}
<svg>
  <filter id="red">
    <feColorMatrix type="matrix" values="
      1 0 0 0 0
      0 0 0 0 0
      0 0 0 0 0
      0 0 0 1 0" />
  </filter>
</svg>

<div></div>

虽然 feColorMatrix 的使用乍一看可能令人望而生畏,但实际上定义您自己的颜色非常简单。以下面的例子代表红色:

1 0 0 0 0 
0 0 0 0 0
0 0 0 0 0
0 0 0 1 0 

如果你从上面获取 粗体 值,我们可以看到它们与 RGBA 颜色模式的关系:

1 - red   (r)
0 - green (g)
0 - blue  (b)
1 - alpha (a)

此处每种颜色都与 0 到 255 除以 255 之间的值相关。因此,如果您有绿色,其 RGBA 为:

 R   G   B  A
(0, 255, 0, 1)

那么您的值将是:

0/255   -> 0 (r)
255/255 -> 1 (g)
0/255   -> 0 (b)
1       -> 1 (a) - don't divide

因此,在我们的矩阵中使用这些值,我们得到:

0 0 0 0 0 
0 1 0 0 0
0 0 0 0 0
0 0 0 1 0 

您现在可以将其添加为附加过滤器(或删除旧过滤器)并给它一个 id 以便它可以在 css filter: url(#filterID)[=31 中被引用=]

查看下面的实施版本:

svg {
  display: none;
}

.red {
  -webkit-filter: url(#red);
  filter: url(#red);
}

.green {
  -webkit-filter: url(#green);
  filter: url(#green);
}
<svg>
  <filter id="red">
    <feColorMatrix type="matrix" values="
      1 0 0 0 0
      0 0 0 0 0
      0 0 0 0 0
      0 0 0 1 0" />
  </filter>
  
  <!--         \/ --- change id to be referenced within css -->
  <filter id="green">
    <feColorMatrix type="matrix" values="
      0 0 0 0 0
      0 1 0 0 0
      0 0 0 0 0
      0 0 0 1 0" />
  </filter>
</svg>

<div class="red"></div>
<div class="green"></div>

<feColorMatrix /> 颜色矩阵生成器:

为方便起见,我制作了一个 feColorMatrix 生成器,它允许您选择或输入颜色以及不透明度,并从该颜色生成 feColorMatrix:

const colorPicker = document.getElementById("color-picker");
const alphaPicker = document.getElementById("alpha-picker");
const codeOutput = document.getElementById("code-output");
const alphaValue = document.getElementById("alpha-value");
const pickedFilter = document.getElementById("picked-filter");
let r=0, g=0, b=0, a=1;

const generateFeColorMatrix = () => `<feColorMatrix type="matrix" values="
  ${r} 0 0 0 0
  0 ${g} 0 0 0
  0 0 0 ${b} 0
  0 0 0 ${a} 0" 
/>
`;

const update = () => {
  const feColorMatrixStr = generateFeColorMatrix();
  codeOutput.textContent = feColorMatrixStr;
  pickedFilter.innerHTML = feColorMatrixStr;
}

colorPicker.addEventListener("input", (e) => {
  const hexparts = e.target.value.match(/(\w{1,2})/g);
  ([r,g,b] = hexparts.map(hex => (parseInt(hex, 16) / 255).toFixed(2))); 
  update();
});

alphaPicker.addEventListener("input", e => {
  a = e.target.value;
  alphaValue.textContent = a;
  update();
});
alphaValue.textContent = a;
svg {
  display: none;
}

#color-picker {
  float: right;
}

#code-output {
  background-color: #f6f6f6; 
}

#emojis {
  -webkit-filter: url(#picked-filter);
  filter: url(#picked-filter);
  font-size: 30px;
}
<input type="color" id="color-picker" />
<input type="range" id="alpha-picker" value="1" min="0" max="1" step="0.01"/><span id="alpha-value">1</span>
<pre id="code-output">
</pre>
<svg>
  <filter id="picked-filter"></filter>
</svg>
<p id="emojis"></p>

请注意目前,此解决方案的浏览器支持非常有限。要查看此功能支持的浏览器的完整列表,请参阅 here

Nick Parsons 的 SVG 矩阵解决方案也可以使用 data: 方案转换为 in-line 解决方案:

filter: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='f'><feColorMatrix color-interpolation-filters='sRGB' type='matrix' values='0 0 0 0 0  0 1 0 0 0  0 0 0 0 0  0 0 0 1 0'/></filter></svg>#f");

color-interpolation-filters='sRGB' 是因为一些浏览器使用不同的默认颜色 space.

我还建议添加灰度滤镜,否则如果使用纯色,某些表情符号会失去对比度:

filter: grayscale(100%) url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='f'><feColorMatrix color-interpolation-filters='sRGB' type='matrix' values='0 0 0 0 0  0 1 0 0 0  0 0 0 0 0  0 0 0 1 0'/></filter></svg>#f");

灰度滤镜也可以用矩阵来表示并合并到SVG数据中。这是一个应该比 CSS 提供的更符合人类感知的:

filter: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='f'><feColorMatrix color-interpolation-filters='linearRGB' type='matrix' values='0.2126 0.7152 0.0722 0 0  0.2126 0.7152 0.0722 0 0  0.2126 0.7152 0.0722 0 0  0 0 0 1 0'/><feColorMatrix color-interpolation-filters='sRGB' type='matrix' values='0 0 0 0 0  0 1 0 0 0  0 0 0 0 0  0 0 0 1 0'/></filter></svg>#f");

最后,这里是一些 PHP 代码,它根据给定的颜色生成矩阵(输入为 CSS hex-based 颜色代码):

function matrixcolor($hex)
{
  list($r, $g, $b) = array_map(function($c) {return $c / 255 / 3;}, sscanf($hex, "#%02x%02x%02x"));
  return "$r $r $r 0 0  $g $g $g 0 0  $b $b $b 0 0";
}

function colormatrix($col)
{
  return "url(\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='f'><feColorMatrix color-interpolation-filters='linearRGB' type='matrix' values='0.2126 0.7152 0.0722 0 0  0.2126 0.7152 0.0722 0 0  0.2126 0.7152 0.0722 0 0  0 0 0 1 0'/><feColorMatrix color-interpolation-filters='sRGB' type='matrix' values='".matrixcolor($col)."  0 0 0 1 0'/></filter></svg>#f\")";
}