css和图片主题色

Posted 恪愚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了css和图片主题色相关的知识,希望对你有一定的参考价值。

这个想法是来源于「性能优化」中的骨架屏:
在图片居多的站点中,这将是非常nice的体验 —— 图片加载通常是比较让人难受的,好的骨架中一般占位图就是低像素的图片,即大体配色和变化是和实际内容一致的。
有时候比如图片不固定的,那可以使用算法获取图片的主体颜色(至少得是同色系的吧),使用纯色块占位。

再进一步想到,在一些“轻松”的场景下,我们可以让背景色/页面主题色跟随轮播图改变。至于效果嘛…你们可以想一下网易云音乐滑动切歌时的背景效果。

因为是不固定图片,所以我想到了四种方法:

  • tensorflow.js 图像色彩分析
  • canvas对图片主基调进行分析,取大概值
  • css高斯模糊
  • 上传图片时后端对图片分析处理,返回时直接返回一张低像素图片

第一种方式目前还在我的实践中,以后会单独出一篇文章;最后一种方式个人不太建议首选:首先后端处理也需要时间,另一方面毕竟也是以图片进行传输的…yee~(而且后端可能也不太建议你首选🤣)

想看实际效果的推荐自己动手试下,因为我发现本文中用QQ截屏截取的图片怎么都这么暗啊,实际展示的还是挺漂亮的。

第三种方式看起来是纯css实现的,怎么获取呢?这就要说到css中的filter: blur(); 简单来说,利用模糊滤镜及进一步拉伸,可以近似地拿到一张图片的主题色:

<div></div>
div 
	background: url(图片地址);
	background-size: cover;
	filter: blur(50px);

你看,通过比较大的一个模糊滤镜,将图片高斯模糊50px,模糊后的图片是不是有点内味了,

图片取自微店APP-微店自营品牌rua娃吧-棉花娃娃,欢迎各位来玩( ̄▽ ̄)"

不过还不行,存在一些模糊边缘,我们可以利用overflow进行剪裁。

接下来,我们需要去掉模糊的边角,以及通过transform: scale()放大效果,将颜色进一步聚焦:
这里就很推荐使用伪元素进行操作了

div 
	position: relative;
	width: xx;
	height: xx;
	overflow: hidden;

div::before 
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: url(图片地址);
	background-size: cover;
	filter: blur(50px);
	transform: scale(2); //自行更改
	transform-origin: center center;

这样就拿到图片的主色调了。当然是要进行其他处理的。

再来说说第二种方法 —— canvas。其实也不建议,因为本身就是JS操作,而在图片又不固定又有些多的情况下单线程的js处理这种“一级事件”造成的性能和体验感的损失是不可想象的。但本文笔者还是要分享一下,因为这是我当初研究的第一个被应用的成果(有情怀了嘿嘿)

首先,canvas中的getImageData()方法可以获取图片的像素集合:

function getImagePixel(canvas, img) 
	const context = canvas.getContext("2d");
	context.drawImage(img, 0, 0);
	return context.getImageData(0, 0, canvas.width, canvas.height).data;

这里对使用canvas不熟悉的同学提个醒:img是异步加载的,所有对图片的操作都要放在 img 的 onload 中进行 —— 你可以考虑用 promise 做这件事。

调用这个函数会拿到一个数组 —— 它是rgba值,也就是说,处理时四个数据为“一组”,更通俗地说,for循环中i+=4!来处理一下数据:

function getCountArr(pData) 
	let colorList = [], rgba = [], rgbaStr = '';
	for(let i=0; i<pData.length; i+=4) 
		rgba[0] = pData[i];
		rgba[1] = pData[i+1];
		rgba[2] = pData[i+2];
		rgba[3] = pData[i+3];
		if(rgba.indexOf(undefined)!==-1 || pData[i+3] === 0) 
			continue;
		
		rgbaStr = rgba.join(',');
		if(rgbaStr in colorList) 
			++colorList[rgbaStr];
		else 
			colorList[rgbaStr] = 1;
		
	
	return colorList;

这个时候,得到的就是每组数据(色值)出现的次数了。
然后改写刚刚的getImagePixel函数:

function getImagePixel(canvas, img) 
	const context = canvas.getContext("2d");
	context.drawImage(img, 0, 0);
	let pixelData = context.getImageData(0, 0, canvas.width, canvas.height).data;
	return getCountArr(pixelData);

至此,我们将其排序并取出第一个值/或者取出某些标志项的平均值,基本上就可以将其作为 background 值啦:

for(let prop in colorList) 
	arr.push(
		color: `rgba($prop)`,
		count: colorList[prop]
	)

arr.sort((a, b)=>return b.count - a.count;);

即可!


峰回路转!

你难道真觉得canvas的这种方法只是鸡肋?那试想这样一种场景:在弱网情况下,图片必定贼慢才能加载出来。这时候我们通过js拿到图片的主色调并填充到图片的位置中。这是不是一个“模糊渐变加载”的绝佳场景!
而且,笔者曾经遇到这样一个场景:往图片上添加文字。这时候你就需要注意一个问题,图片主色调。用canvas分析图片的主要颜色或平均色可以在深色调时添加白色文字在浅色调时添加黑色文字!

以上是关于css和图片主题色的主要内容,如果未能解决你的问题,请参考以下文章

Vue3+Antd 修改antd主题色,配置全局css

前端切换主题

css3 根据用户操作系统的主题色改变自己网站的颜色

css3 根据用户操作系统的主题色改变自己网站的颜色

从网易云音乐的背景聊聊如何对图片主题色进行提取

css 图片自身的背景色如何去除