likes
comments
collection
share

提取图片主题色

作者站长头像
站长
· 阅读数 14

背景

提取图片主题色

产品说,这里的卡片背景颜色是白色的,大背景也是白色的,没有区分度,能不能把图片中最多的颜色提取出来作为卡片的背景色

虽然刚开始听到这个需求我是懵逼的,但是,经过了一番资料查阅,还是找到了几个实现方法:

1. 利用canvas实现

我们知道图片是由一个个像素点组成的,通过canvasgetImageData()方法我们可以获取到图片的像素数据 主要步骤为:

  • 获取图片绘制到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 });
        }
    })
}

实现效果

提取图片主题色

存在问题

这种方法虽然能提取到图片的主题色,但是存在着几个问题:

  1. 图片的加载需要一定时间
  2. 图片颜色获取这里需要遍历所有的像素点,越大的图片遍历所需时间也越多

因此,我们的页面中用户会看到卡片背景颜色的变化,不符合产品预期

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; // 设置透明度
}

实现效果

提取图片主题色

存在问题

  1. 之能拿到大致的主题色,不够精确
  2. 模糊滤镜本身是比较消耗性能的,如果一个页面存在多个这种方法获取到的背景,可能对性能会造成一定的影响

当然这里对性能的影响已经比利用canvas分析图片像素好很多了,至少在页面上不会看到颜色变化过程了

参考文章

blog.51cto.com/u_15296224/…

www.cnblogs.com/coco1s/p/14…

www.cnblogs.com/cangqinglan…

转载自:https://juejin.cn/post/7272181868073582604
评论
请登录