在一般用户popover中都有一个横幅的设计。大概就是这样:
这个设计的目的是让用户可以上传自己的横幅图片,来达到个性化的设计。
但是如果没有横幅的情况下,如果也想让用户有一定个性化的能力,要怎么做呢?
Tailchat 的做法基于头像颜色计算主要配色方案,来确保整体配色和谐的前提下来确保不同用户的个性化
实现的方式非常简单,简单的说就是我们把头像放到一个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. 即我们可以视为该图片的主要颜色。
当然,该算法准确来说其实是取平均色,一个极端的例子是比如一张图有一般是红色,一半是绿色,那么最后返回的结果是黄色。当然了,大部分情况下不会出现这种比较极端的例子,在大部分情况下都是比较和谐的。
本文实战用法见:
我会不定期的从 Tailchat 的源码中拆出一些实用的实战小技巧分享给大家,关注我,并给 Tailchat 点点 star。我将带你以实战理解算法的妙用。