提取图片主题色

背景

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

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

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

相关推荐
远山无期4 小时前
解决Tailwind任意值滥用:规范化CSS开发体验
前端·css·eslint
玉米Yvmi7 小时前
从零理解 CSS 弹性布局:轻松掌控页面元素排布
前端·javascript·css
西洼工作室7 小时前
前端js汉字手写练习系统
前端·javascript·css
Flystone9 小时前
CSS 有什么奇技淫巧?
css
我血条子呢9 小时前
【CSS】类似渐变色弯曲border
前端·css
用户1887871069849 小时前
CSS3 clip-path+animation实现不规则容器中的粒子下落
css
用户1887871069849 小时前
CSS3 实现16:9大屏居中显示
css
海市公约9 小时前
CSS 核心知识点精讲:基础概念、样式规则与布局技巧
前端·css·盒子模型·选择器·网页布局·网页样式设计
elangyipi1239 小时前
使用CSS Contain 优化你的页面(重排和重绘)
前端·css
未来可期wlkq9 小时前
overflow跟input搭配使用,会导致内容区整体移动,overflow属性导致
javascript·css·vue.js