JS根据图片主色调设置背景色

1,236 阅读1分钟

具体实现

主要思路

先将一张大图先缩小为一张小图,然后利用canvasgetImageData方法 提取图片的像素,然后再计算颜色的出现频率,然后再进行频率的排序

  • 加载图片,并进行缩小

    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

扫码_搜索联合传播样式-标准色版.png