是否有可能获得表情符号的主要颜色?

Is it possible to get the majority color of an emoji?

我想要实现的是向表情符号添加 text-shadow,其中 text-shadow 颜色是表情符号中最突出的颜色。

我知道有 JavaScript 库可以识别图像中最突出的颜色,但由于表情符号在技术上是文本,所以我不确定我将如何做,甚至不知道是否可能。

我会提供一些我已经尝试过的代码,但老实说,我什至不知道从哪里开始。

.emoji {
  text-shadow: 0px 0px 20px rgba(54, 169, 230, 0.65);
  padding: 3px 6px;
  font-size: 24px;
}
<span class="emoji"></span>

另一种不使用 text-shadow 的方法是复制表情符号,将其直接放在原始表情符号的后面并对其进行模糊处理。

您可以直接在 HTML 中创建副本,或者如果使用 js 更方便,如下例所示。

var emoji = document.querySelector("#blur-me");
var copy = emoji.cloneNode(true);
copy.classList.remove("emoji");
emoji.appendChild(copy);
.emoji {
  padding: 3px 6px;
  font-size: 24px;
  position: relative;
  display: inline-block;
}

.emoji span {
  position: absolute;
  left: 6px;
  z-index: -1;
  filter: blur(10px);
}
<span class="emoji" id="blur-me"></span>

提出的替代答案是使用属性复制图标而不需要 JS。您还可以应用缩放变换使效果更明显:

.emoji {
  padding: 3px 6px;
  margin:0 5px;
  font-size: 24px;
  position: relative;
  display: inline-block;
  z-index: 0;
}
.emoji:before,
.emoji:after{
  content: attr(data-icon);
}
.emoji:before {
  position: absolute;
  z-index: -1;
  filter: blur(3px);
  transform: scale(1.5);
}
<span class="emoji" data-icon=""></span>
<span class="emoji" data-icon=""></span>
<span class="emoji" data-icon=""></span>
<span class="emoji" data-icon=""></span>

模糊表情符号副本的方法(由 sol 提出)是一种有效且相当轻量级的方法。稍微调整一下,您可以获得更像阴影的东西:

var emoji = document.querySelector("#blur-me");
var copy = emoji.cloneNode(true);
copy.classList.remove("emoji");
emoji.appendChild(copy);
.emoji {
  padding: 3px 6px;
  font-size: 24px;
  position: relative;
  display: inline-block;
}

.emoji span {
  position: absolute;
  left: 6px;
  z-index: -1;
  filter: blur(5px);
  transform: scale(.95) translate(5px, 5px);
}
<span class="emoji" id="blur-me"></span>

scale(.95) 是为了补偿它增长的模糊,然后 translate() 只是简单地将它移动到更像文本阴影而不是模糊的地方。

另一种可以让您获得更准确颜色的方法是将其绘制到 canvas。一旦你有了一个 canvas,它基本上是一个图像,所以你提到的那些库可以工作。

const canvas = document.querySelector('canvas');
canvas.width = 30;
canvas.height = 30;
const ctx = canvas.getContext('2d');
ctx.font = "20px Arial";
ctx.fillText("", 0, 20);
canvas {
  border: 1px solid #000;
}

.emoji {
  text-shadow: rgba(0, 0, 0, .5) 2px 2px 2px;
}
<div class="emoji"></div>
<canvas></canvas>

获得平均值后,您可以通过几种不同的方式获得平均值。一种是只获取原始像素,排除背景颜色,然后对剩余部分进行数学平均。由于 canvas 是不透明的,您可以使用老派的技巧填充非常难看的颜色:

const canvas = document.querySelector('canvas');
canvas.width = 30;
canvas.height = 30;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#121212'; // 12 => 17 in decimal
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.font = "20px Arial";
ctx.fillText("", 0, 20);

const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// an array in the format [r, g, b, count] decimal
const sums = imageData.data.reduce((r, v, i) => {
   if (v === 18) return r; // our fill color, skip
   if (i % 4 !== 3) { // i % 4 === 3 is transparent, ignoring
     r[i % 4] += v;
     r[3]++;
   }
   
   return r;
}, [0, 0, 0, 0]);

sums[3] = sums[3] / 3; // divide by 3 since we counted each pixel 3 times
const averages = [Math.floor(sums[0] / sums[3]), Math.floor(sums[1] / sums[3]), Math.floor(sums[2] / sums[3])];

// just to see the color
ctx.fillStyle = `rgb(${averages.join(',')})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
canvas {
  border: 1px solid #000;
}

.emoji {
  text-shadow: rgba(0, 0, 0, .5) 2px 2px 2px;
}
<div class="emoji"></div>
<canvas></canvas>

显然,这个特定的算法非常基础,并不完美(主要用于演示目的)。还有其他算法或库。如果您调用 canvas.toDataUrl(),您甚至会得到一个值,您可以在大多数库中像图像一样使用它。

从那里,只需使用计算出的颜色,然后使用 JavaScript 将其设置为 element.style.textShadow 属性即可。

document.querySelector('.emoji').style.textShadow = 'rgb(255, 0, 0) 2px 2px';
<span class="emoji"></span>