提取图片主题色
背景

产品说,这里的卡片背景颜色是白色的,大背景也是白色的,没有区分度,能不能把图片中最多的颜色提取出来作为卡片的背景色
虽然刚开始听到这个需求我是懵逼的,但是,经过了一番资料查阅,还是找到了几个实现方法:
1. 利用canvas
实现
我们知道图片是由一个个像素点组成的,通过canvas
的getImageData()
方法我们可以获取到图片的像素数据
主要步骤为:
- 获取图片绘制到canvas画布上
- 获取图片每个像素点的rgb值
- 对所有rbg值进行统计
- 在统计值中找到最多的rbga值
示例
图片的加载是异步行为,我们需要在图片的onload方法里对对图片进行分析,因此这里我们封装了一个异步函数
function getThemeColor(imgUrl: string): Promise<{ backgroundColor: string }> {
return new Promise<{ backgroundColor: string }>((resolve) => {
const image = new Image();
image.src = imgUrl;
image.crossOrigin = ''; // 解决跨域问题
image.onload = function() {
const shrinkWidth = image.width / 10; // 图片宽度压缩比例
const shrinkHeight = image.height / 10; //图片高度压缩比例
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
/** 绘制图片 */
context.drawImage(image, 0, 0, shrinkWidth, shrinkHeight);
/** 获取图片像素点rgb数据 */
const pixels = imageData.data;
/** 对像素点数据进行分析找到最多的颜色 */
const colorCount: {
[key: string]: number;
} = {};
let maxCount = 0;
let maxColor = '';
let maxR = 255;
let maxG = 255;
let maxB = 255;
for (let i = 0; i < pixels.length; i += 4) {
/**
* 这里因为我需要设置图片的透明度因此对r,g,b三个数据进行了分别记录
* 如果只需要颜色则不需要分别记录直接记录color即可
*/
const r = pixels[i];
const g = pixels[i + 1];
const b = pixels[i + 2];
const color = `rgb(${r},${g},${b})`;
if (colorCount[color]) {
colorCount[color] = colorCount[color] + 1;
} else {
colorCount[color] = 1;
}
if (colorCount[color] > maxCount) {
maxCount = colorCount[color];
maxColor = color;
maxR = r;
maxG = g;
maxB = b;
}
}
console.log(maxColor, 'color');
backgroundColor = `rgba(${maxR}, ${maxG}, ${maxB}, .05)`;
resolve({ backgroundColor });
}
})
}
实现效果

存在问题
这种方法虽然能提取到图片的主题色,但是存在着几个问题:
- 图片的加载需要一定时间
- 图片颜色获取这里需要遍历所有的像素点,越大的图片遍历所需时间也越多
因此,我们的页面中用户会看到卡片背景颜色的变化,不符合产品预期
2. 利用colorthief
库实现
这个库是基于JavaScript和canvas来用于提取图片的主要颜色或者代表性颜色调色板的工具,它的也是基于canvas分析图片像素实现的。
使用示例
安装:
$ npm i --save colorthief
使用:
import ColorThief from "colorthief";
const image = new Image();
image.src = "imgUrl";
image.crossOrigin = '';
image.onload = function() {
colorthief = new ColorThief();
const dominantColor = colorThief.getColor(image);
console.log("主色调为:", dominantColor);
}
存在问题
colorthief的实现原理也是对图片像素点进行分析,且这里也需要先加载图片,因此它存在的问题同上一个方法的问题
利用CSS filter属性
实现
CSS **filter
** 属性将模糊或颜色偏移等图形效果应用于元素。滤镜通常用于调整图像、背景和边框的渲染。
filter: blur();
将高斯模糊应用于输入图像。
利用模糊滤镜作用给图片:
div {
background: url("xxx");
background-size: cover;
filter: blur(50px);
}
我们通过比较大的一个模糊滤镜,可以看到基本能看到图片的主题颜色了
示例
这里我给之前的卡片div添加了一个伪元素,通过将伪元素的背景图片设置为卡片的背景图,同时对它进行模糊和放大处理 js:
/** 我们需要能够在css中获取卡片的背景图地址,这里我们通过js给style添加计算属性 */
cardRef.current?.style.setProperty('--img-url', `url(${detailMsg.imgUrl})`);
css:
.card_img {
position: relative;
}
.card_img::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: var(--img-url); // 这里我们就可以拿到背景图地址了
background-size: cover;
filter: blur(50px); // 模糊处理
transform: scale(3); // 放大处理
opacity: 0.1; // 设置透明度
}
实现效果

存在问题
- 之能拿到大致的主题色,不够精确
- 模糊滤镜本身是比较消耗性能的,如果一个页面存在多个这种方法获取到的背景,可能对性能会造成一定的影响
当然这里对性能的影响已经比利用canvas分析图片像素好很多了,至少在页面上不会看到颜色变化过程了
参考文章
转载自:https://juejin.cn/post/7272181868073582604