具体实现
主要思路
先将一张大图先缩小为一张小图,然后利用canvas的getImageData方法 提取图片的像素,然后再计算颜色的出现频率,然后再进行频率的排序
-
加载图片,并进行缩小
const img = new Image(); img.src = imgSrc; await new Promise((resolve) => { img.onload = resolve; }); // 图片缩小 let shrinkFactor = 10; if (img.width > 300) { shrinkFactor = img.width / 300; } let height = img.height / shrinkFactor; let width = img.width / shrinkFactor; -
创建
canvas,并获取所有像素const canvas = document.createElement('canvas'); canvas.setAttribute('width', `${width}px`); canvas.setAttribute('height', `${height}px`); const ctx: any = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, width, height); imgUrl = canvas.toDataURL('image/jpeg', 1); let originalPiexls; try { //保存像素 originalPiexls = ctx.getImageData(0, 0, width, height).data; let colors= getColor(originalPiexls); themeColor=colors[0] } catch (error) { console.log(error); } -
处理获取到的所有像素,获取图片的主色调
-
第一种方式,直接获取图片中出现次数最多的一种颜色,但是很有可能不是自己想要的
const getColor = (colorData) => { const matrix: any = Array.from(colorData); let colorArr: any = []; let colorList = {}; let i = 0; while (i < matrix.length) { const r = matrix[i]; const g = matrix[i + 1]; const b = matrix[i + 2]; const a = matrix[i + 3]; i = i + 4; // 最后 +4 比每次 i++ 快 10ms 左右性能 const key = [r, g, b, a].join(','); key in colorList ? ++colorList[key] : (colorList[key] = 1); } for (let key in colorList) { colorArr.push({ rgba: `rgba(${key})`, num: colorList[key], }); } colorArr = colorArr.sort((a, b) => { return b.num - a.num; }); console.log('🚀 ~ file: useHandleImg.ts:60 ~ getColor ~ colorArr:', colorArr); return colorArr; };返回的数组即是按出现次数从大到小排序的颜色值,第一个就是出现次数最多的
-
第二种方式,使用中值切割算法,获取颜色值
const getColor = (colorData) => { const matrix: any = Array.from(colorData); let colorArr: any = []; let colorList = {}; const pixelArray:any = []; let i = 0; while (i < matrix.length) { const r = matrix[i]; const g = matrix[i + 1]; const b = matrix[i + 2]; const a = matrix[i + 3]; i = i + 4; // 最后 +4 比每次 i++ 快 10ms 左右性能 if (typeof a === 'undefined' || a >= 125) { if (!(r > 250 && g > 250 && b > 250)) { pixelArray.push([r, g, b]); } } const key = [r, g, b, a].join(','); key in colorList ? ++colorList[key] : (colorList[key] = 1); } // 发送数组到聚类值的量化函数,使用中值切割算法 const cmap = dataHandler.quantize(pixelArray, 3); const palette = cmap? cmap.palette() : null; console.log("🚀 ~ file: useHandleImg.ts:69 ~ getColor ~ palette:", palette) return palette };使用中值切割算法的量化函数,代码较多,完整代码请看下面链接
返回的数组即是筛选出的颜色值,第一个就是想要的主题色
-
完整代码 https://github.com/llzzii/vue3-template/blob/main/src/hooks/useHandleImg.ts