提取图片主题色

背景

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

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

1. 利用canvas实现

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

  • 获取图片绘制到canvas画布上
  • 获取图片每个像素点的rgb值
  • 对所有rbg值进行统计
  • 在统计值中找到最多的rbga值

示例

图片的加载是异步行为,我们需要在图片的onload方法里对对图片进行分析,因此这里我们封装了一个异步函数

js 复制代码
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分析图片像素实现的。

使用示例

安装:

shell 复制代码
$ npm i --save colorthief

使用:

js 复制代码
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();将高斯模糊应用于输入图像。

利用模糊滤镜作用给图片:

css 复制代码
div {
    background: url("xxx");
    background-size: cover;
    filter: blur(50px);
}

我们通过比较大的一个模糊滤镜,可以看到基本能看到图片的主题颜色了

示例

这里我给之前的卡片div添加了一个伪元素,通过将伪元素的背景图片设置为卡片的背景图,同时对它进行模糊和放大处理 js:

js 复制代码
/** 我们需要能够在css中获取卡片的背景图地址,这里我们通过js给style添加计算属性 */
cardRef.current?.style.setProperty('--img-url', `url(${detailMsg.imgUrl})`);

css:

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...

相关推荐
软件技术NINI9 分钟前
MATLAB疑难诊疗:从调试到优化的全攻略
javascript·css·python·html
~无忧花开~1 小时前
CSS学习笔记(五):CSS媒体查询入门指南
开发语言·前端·css·学习·媒体
嬉皮客3 小时前
TailwindCSS 初探
前端·css
昔人'13 小时前
css使用 :where() 来简化大型 CSS 选择器列表
前端·css
昔人'13 小时前
css `dorp-shadow`
前端·css
東風20 小时前
色彩剧场:当CSS变量登上深色模式的舞台
css
昔人'1 天前
css`scrollbar-gutter`防止滚动条可见性变化时发生布局偏移
前端·css
在下Z.1 天前
前端基础--css(1)
前端·css
小帆聊前端1 天前
CSS 选择器全解析:从基础语法到组件库样式修改,解决前端样式定位难题
css
某柚啊1 天前
iOS移动端H5键盘弹出时页面布局异常和滚动解决方案
前端·javascript·css·ios·html5