获取图片主题色

获取图片主题色

  • 一、问题描述
  • 二、具体实现
    • 1. 图片读取
    • 2. 使用Canvas提取图片的像素数据
  • 3. 聚合处理
  • 三、全部代码

一、问题描述

当我们遇到根据背景图片来修改页面样式的时候,可能就需要去实现获取图片主题色的功能。比如深色背景配白色导航栏,浅色背景配黑色导航栏这种。

二、具体实现

1. 图片读取

根据url将图片读取到dom中,方便后续操作

  const imageUrl = 'home_bg.png' // 你的图片地址
  const sourceImage = document.createElement("img");
  sourceImage.addEventListener('load' , () => {
    console.log(111);
    // TODO
  });
  sourceImage.src = imageUrl

2. 使用Canvas提取图片的像素数据

我们可以利用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)

获取图片主题色_第1张图片
得到的数据是很多像素点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);

获取图片主题色_第2张图片

3. 聚合处理

这里的数据我们可以自己做聚合、分析等等处理方式进行操作,最后得到我们自己想要的值。有兴趣可以自行去了解,我这里就使用了一个现成的库 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() : []);

获取图片主题色_第3张图片
最后可以选择对前五的颜色进行加权处理,也可以直接取排第一的颜色(根据业务要求自行判断)

三、全部代码

记得首先引入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);
})

你可能感兴趣的:(JavaScript学习笔记,ES6学习笔记,javascript,前端,es6)