前端实用小技巧: 计算图片主要颜色

346 阅读1分钟

在一般用户popover中都有一个横幅的设计。大概就是这样:

image.png

这个设计的目的是让用户可以上传自己的横幅图片,来达到个性化的设计。

但是如果没有横幅的情况下,如果也想让用户有一定个性化的能力,要怎么做呢?

Tailchat 的做法基于头像颜色计算主要配色方案,来确保整体配色和谐的前提下来确保不同用户的个性化

image.png

实现的方式非常简单,简单的说就是我们把头像放到一个canvas里,然后通过canvas提取每个颜色的像素信息,通过算法计算出平均值。

废话不多说我们直接上代码

/**
 * 加载图片
 */
async function loadImage(url: string): Promise<HTMLImageElement> {
  const el = document.createElement('img');
  return new Promise((resolve, reject) => {
    el.onload = () => resolve(el);
    el.onerror = reject;
    el.src = url;
    el.crossOrigin = 'Anonymous';
  });
}

/**
 * 获取图片的主要颜色
 */
export async function fetchImagePrimaryColor(imageUrl: string) {
  const img = await loadImage(imageUrl);
  const canvas = document.createElement('canvas');

  canvas.width = img.width;
  canvas.height = img.height;
  const ctx = canvas.getContext('2d');
  if (!ctx) {
    return { r: 0, g: 0, b: 0, a: 0 };
  }

  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

  let r = 0;
  let g = 0;
  let b = 0;
  let a = 0;

  for (let row = 0; row < imageData.height; row++) {
    for (let col = 0; col < imageData.width; col++) {
      r += imageData.data[(imageData.width * row + col) * 4];
      g += imageData.data[(imageData.width * row + col) * 4 + 1];
      b += imageData.data[(imageData.width * row + col) * 4 + 2];
      a += imageData.data[(imageData.width * row + col) * 4 + 3];
    }
  }

  const sum = imageData.width * imageData.height;
  r = Math.round(r / sum);
  g = Math.round(g / sum);
  b = Math.round(b / sum);
  a = Math.round(a / sum);

  return {
    r,
    g,
    b,
    a,
  };
}

工具函数 fetchImagePrimaryColor 接受一个图片的地址,通过 loadImage 函数加载图片并返回一个图片dom

根据返回的 img dom 的宽高创建canvas的宽高,然后将图片通过 ctx.drawImage(img, 0, 0, canvas.width, canvas.height) 直接绘制到 canvas 中.

然后通过 ctx.getImageData(0, 0, canvas.width, canvas.height); 获取到该图片的完整像素信息。

我们通过两个循环分别获取到该图片所有位置中的 rgba 信息,计算其总和并取其平均数,最后返回出该图片的平均 rgba. 即我们可以视为该图片的主要颜色。

当然,该算法准确来说其实是取平均色,一个极端的例子是比如一张图有一般是红色,一半是绿色,那么最后返回的结果是黄色。当然了,大部分情况下不会出现这种比较极端的例子,在大部分情况下都是比较和谐的。


本文实战用法见:

github.com/msgbyte/tai…

我会不定期的从 Tailchat 的源码中拆出一些实用的实战小技巧分享给大家,关注我,并给 Tailchat 点点 star。我将带你以实战理解算法的妙用。