当我们遇到根据背景图片来修改页面样式的时候,可能就需要去实现获取图片主题色的功能。比如深色背景配白色导航栏,浅色背景配黑色导航栏这种。
根据url将图片读取到dom中,方便后续操作
const imageUrl = 'home_bg.png' // 你的图片地址
const sourceImage = document.createElement("img");
sourceImage.addEventListener('load' , () => {
console.log(111);
// TODO
});
sourceImage.src = imageUrl
我们可以利用Canvas来获取图片的全部像素数据
基于上面TODO部分:
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const width = canvas.width = sourceImage.naturalWidth;
const height = canvas.height = sourceImage.naturalHeight;
context.drawImage(sourceImage, 0, 0, width, height);
const imageData = context.getImageData(0, 0, width, height)
console.log(imageData)
得到的数据是很多像素点RGBA数据依次排列组成的数组:
R - 红色(0-255)
G - 绿色(0-255)
B - 蓝色(0-255)
A - alpha 通道(0-255; 0 是透明的,255 是完全可见的)
所以我们要对数据进行处理,得到每个像素点的数据
const pixelCount = width * height;
const pixels = imageData.data;
const pixelArray = [];
const quality = 10 // 间隔,如果图片很大时每个像素值都取可能会对性能有所影响,所以按自己需求设置间隔多少像素点取一个值
for (let i = 0, offset, r, g, b, a; i < pixelCount; i = i + quality) {
offset = i * 4;
r = pixels[offset + 0];
g = pixels[offset + 1];
b = pixels[offset + 2];
a = pixels[offset + 3];
// 如果像素点透明则不取值
if (typeof a === 'undefined' || a >= 125) {
pixelArray.push([r, g, b]);
}
}
console.log(pixelArray);
这里的数据我们可以自己做聚合、分析等等处理方式进行操作,最后得到我们自己想要的值。有兴趣可以自行去了解,我这里就使用了一个现成的库 quantize.js
。https://www.npmjs.com/package/quantize。插件源码我已经附在文档上了。
具体实现:
首先引入quantize.js(模块化的项目中可以使用import或者require,这里只是方便演示)
<script src="quantize.js">script>
const quantize = MMCQ.quantize
const colorCount = 5 // 需要聚合出前五的颜色
const cMap = quantize(pixelArray, colorCount);
console.log(cMap)
console.log(cMap ? cMap.palette() : []);
最后可以选择对前五的颜色进行加权处理,也可以直接取排第一的颜色(根据业务要求自行判断)
记得首先引入quantize.js
<script src="quantize.js">script>
const quantize = MMCQ.quantize
// canvas实例,方便获取图片像素数据
class CanvasImage {
canvas = document.createElement('canvas')
context = this.canvas.getContext('2d')
constructor(image) {
this.width = this.canvas.width = image.naturalWidth;
this.height = this.canvas.height = image.naturalHeight;
this.context.drawImage(image, 0, 0, this.width, this.height);
}
getImageData() {
return this.context.getImageData(0, 0, this.width, this.height);
};
}
class ColorStat {
quality = 10
/**
*
* @param quality 间隔多少个像素点取一个数据
*/
constructor(quality = 10) {
this.quality = quality
}
/**
* @description 提取图片像素点函数
* @param imgData 图片像素数据 多组 r、g、b、a 数据组成的一维数组
* @param pixelCount 一共多少个像素点
* @param quality 间隔多少个像素点取一个数据
* @returns {number[][]}
*/
createPixelArray(imgData, pixelCount, quality) {
const pixels = imgData;
const pixelArray = [];
// 间隔取对应的像素值
for (let i = 0, offset, r, g, b, a; i < pixelCount; i = i + quality) {
offset = i * 4;
r = pixels[offset];
g = pixels[offset + 1];
b = pixels[offset + 2];
a = pixels[offset + 3];
// 如果像素点透明则不取值(透明点不影响主题色)
if (typeof a === 'undefined' || a >= 125) {
pixelArray.push([r, g, b]);
}
}
return pixelArray;
}
/**
* 获取聚合后的像素值
* @param sourceImage 图片dom
* @param colorCount 要取的聚合后前几个像素值
* @returns {*|*[]}
*/
getPalette(sourceImage, colorCount) {
// 创建canvas实例
const image = new CanvasImage(sourceImage);
const imageData = image.getImageData();
const pixelCount = image.width * image.height;
const pixelArray = this.createPixelArray(imageData.data, pixelCount, this.quality);
// 使用 quantize.js 聚合统计数据
const cmap = quantize(pixelArray, colorCount);
return cmap ? cmap.palette() : [];
}
/**
* 通过图片地址获取图片主题色
* @param imageUrl 图片地址(模块化项目本地图片需要使用 require(url)
* @param colorCount 要取的聚合后前几个像素值
* @returns {Promise}
*/
getColorFromUrl(imageUrl, colorCount = 5) {
return new Promise((resolve, reject) => {
// 创建img dom实例
const sourceImage = document.createElement("img");
sourceImage.addEventListener('load' , () => {
const palette = this.getPalette(sourceImage, colorCount);
if (palette) {
resolve(palette)
} else {
reject('图片异常!')
}
});
sourceImage.src = imageUrl
})
}
}
const colorStat = new ColorStat()
colorStat.getColorFromUrl('./home_bg.png', 5).then(data => {
console.log(data);
})