提取图片主题色

背景

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

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

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

相关推荐
大漠_w3cpluscom1 小时前
使用 clip-path: shape() 创建 Squircle 形状
前端·css·weui
会员源码网2 天前
告别参数混乱:如何优雅解决方法参数过多导致的可维护性难题
css
Lee川3 天前
现代Web开发中的CSS继承、Flexbox布局与LocalStorage交互:从文档解析到实践应用
前端·css
helloweilei4 天前
CSS进阶: background-clip
css
DeathGhost4 天前
CSS container容器查询
前端·css
不会敲代码15 天前
前端组件化样式隔离实战:React CSS Modules、styled-components 与 Vue scoped 对比
css·vue.js·react.js
Sailing5 天前
🚀 别再乱写 16px 了!CSS 单位体系已经进入“计算时代”,真正的响应式布局
前端·css·面试
球球pick小樱花6 天前
游戏官网前端工具库:海内外案例解析
前端·javascript·css
AAA阿giao7 天前
从零构建一个现代登录页:深入解析 Tailwind CSS + Vite + Lucide React 的完整技术栈
前端·css·react.js
掘金安东尼7 天前
用 CSS 打造完美的饼图
前端·css