如何仅使用CSS滤镜将黑色转换为任何给定的颜色

前端之家收集整理的这篇文章主要介绍了如何仅使用CSS滤镜将黑色转换为任何给定的颜色前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我的问题是:给定目标RGB颜色,使用仅 CSS filters将黑色(#000)重新着色为该颜色的公式是什么?

要接受答案,需要提供一个函数(以任何语言)接受目标颜色作为参数并返回相应的CSS过滤字符串.

对此的上下文是需要在背景图像内重新着色SVG.在这种情况下,它是支持KaTeX:https://github.com/Khan/KaTeX/issues/587中的某些TeX数学特征.

如果目标颜色是#ffff00(黄色),则一个正确的解决方案是:

filter: invert(100%) sepia() saturate(10000%) hue-rotate(0deg)

(demo)

非目标

>动画.
>非CSS过滤解决方案.
>从黑色以外的颜色开始.
>关心黑色以外的颜色会发生什么.

结果到目前为止

>强力搜索固定过滤器列表的参数:https://stackoverflow.com/a/43959856/181228
缺点:低效,只产生一些16,777,216种可能的颜色(676,248,hueRotateStep = 1).
>使用SPSA的更快的搜索解决方案:
https://stackoverflow.com/a/43960991/181228
赏金奖励
>一个阴影解决方案:
https://stackoverflow.com/a/43959853/181228
缺点:不适用于Edge.需要非过滤器CSS更改和次要HTML更改.

您仍然可以通过提交非暴力解决方案获得接受的答案!

资源

>如何计算色调旋转和棕褐色:
https://stackoverflow.com/a/29521147/181228
示例Ruby实现:

LUM_R = 0.2126; LUM_G = 0.7152; LUM_B = 0.0722
HUE_R = 0.1430; HUE_G = 0.1400; HUE_B = 0.2830

def clamp(num)
  [0,[255,num].min].max.round
end

def hue_rotate(r,g,b,angle)
  angle = (angle % 360 + 360) % 360
  cos = Math.cos(angle * Math::PI / 180)
  sin = Math.sin(angle * Math::PI / 180)
  [clamp(
     r * ( LUM_R  +  (1 - LUM_R) * cos  -  LUM_R * sin       ) +
     g * ( LUM_G  -  LUM_G * cos        -  LUM_G * sin       ) +
     b * ( LUM_B  -  LUM_B * cos        +  (1 - LUM_B) * sin )),clamp(
     r * ( LUM_R  -  LUM_R * cos        +  HUE_R * sin       ) +
     g * ( LUM_G  +  (1 - LUM_G) * cos  +  HUE_G * sin       ) +
     b * ( LUM_B  -  LUM_B * cos        -  HUE_B * sin       )),clamp(
     r * ( LUM_R  -  LUM_R * cos        -  (1 - LUM_R) * sin ) +
     g * ( LUM_G  -  LUM_G * cos        +  LUM_G * sin       ) +
     b * ( LUM_B  +  (1 - LUM_B) * cos  +  LUM_B * sin       ))]
end

def sepia(r,b)
  [r * 0.393 + g * 0.769 + b * 0.189,r * 0.349 + g * 0.686 + b * 0.168,r * 0.272 + g * 0.534 + b * 0.131]
end

请注意,上面的夹子使色调旋转功能非线性.

浏览器实现:Chromium,Firefox.
>演示:从灰度颜色获取非灰度颜色:
https://stackoverflow.com/a/25524145/181228
>几乎有效的公式:
https://stackoverflow.com/a/29958459/181228
>详细解释为什么上面的公式是错误的(CSS色调旋转不是真正的色调旋转而是线性近似):
https://stackoverflow.com/a/19325417/2441511

解决方法

这是兔子洞的一次旅行,但在这里!
var tolerance = 1;
var invertRange = [0,1];
var invertStep = 0.1;
var sepiaRange = [0,1];
var sepiaStep = 0.1;
var saturateRange = [5,100];
var saturateStep = 5;
var hueRotateRange = [0,360];
var hueRotateStep = 5;
var possibleColors;
var color = document.getElementById('color');
var pixel = document.getElementById('pixel');
var filtersBox = document.getElementById('filters');
var button = document.getElementById('button');
button.addEventListener('click',function() { 			      
	getNewColor(color.value);
})

// matrices taken from https://www.w3.org/TR/filter-effects/#feColorMatrixElement
function sepiaMatrix(s) {
	return [
		(0.393 + 0.607 * (1 - s)),(0.769 - 0.769 * (1 - s)),(0.189 - 0.189 * (1 - s)),(0.349 - 0.349 * (1 - s)),(0.686 + 0.314 * (1 - s)),(0.168 - 0.168 * (1 - s)),(0.272 - 0.272 * (1 - s)),(0.534 - 0.534 * (1 - s)),(0.131 + 0.869 * (1 - s)),]
}

function saturateMatrix(s) {
	return [
		0.213+0.787*s,0.715-0.715*s,0.072-0.072*s,0.213-0.213*s,0.715+0.285*s,0.072+0.928*s,]
}

function hueRotateMatrix(d) {
	var cos = Math.cos(d * Math.PI / 180);
	var sin = Math.sin(d * Math.PI / 180);
	var a00 = 0.213 + cos*0.787 - sin*0.213;
	var a01 = 0.715 - cos*0.715 - sin*0.715;
	var a02 = 0.072 - cos*0.072 + sin*0.928;

	var a10 = 0.213 - cos*0.213 + sin*0.143;
	var a11 = 0.715 + cos*0.285 + sin*0.140;
	var a12 = 0.072 - cos*0.072 - sin*0.283;

	var a20 = 0.213 - cos*0.213 - sin*0.787;
	var a21 = 0.715 - cos*0.715 + sin*0.715;
	var a22 = 0.072 + cos*0.928 + sin*0.072;

	return [
		a00,a01,a02,a10,a11,a12,a20,a21,a22,]
}

function clamp(value) {
	return value > 255 ? 255 : value < 0 ? 0 : value;
}

function filter(m,c) {
	return [
		clamp(m[0]*c[0] + m[1]*c[1] + m[2]*c[2]),clamp(m[3]*c[0] + m[4]*c[1] + m[5]*c[2]),clamp(m[6]*c[0] + m[7]*c[1] + m[8]*c[2]),]
}

function invertBlack(i) {
	return [
		i * 255,i * 255,]
}

function generateColors() {
	let possibleColors = [];

	let invert = invertRange[0];
	for (invert; invert <= invertRange[1]; invert+=invertStep) {
		let sepia = sepiaRange[0];
		for (sepia; sepia <= sepiaRange[1]; sepia+=sepiaStep) {
			let saturate = saturateRange[0];
			for (saturate; saturate <= saturateRange[1]; saturate+=saturateStep) {
				let hueRotate = hueRotateRange[0];
				for (hueRotate; hueRotate <= hueRotateRange[1]; hueRotate+=hueRotateStep) {
					let invertColor = invertBlack(invert);
					let sepiaColor = filter(sepiaMatrix(sepia),invertColor);
					let saturateColor = filter(saturateMatrix(saturate),sepiaColor);
					let hueRotateColor = filter(hueRotateMatrix(hueRotate),saturateColor);

					let colorObject = {
						filters: { invert,sepia,saturate,hueRotate },color: hueRotateColor
					}

					possibleColors.push(colorObject);
				}
			}
		}
	}

	return possibleColors;
}

function getFilters(targetColor,localTolerance) {
	possibleColors = possibleColors || generateColors();

	for (var i = 0; i < possibleColors.length; i++) {
		var color = possibleColors[i].color;
		if (
			Math.abs(color[0] - targetColor[0]) < localTolerance &&
			Math.abs(color[1] - targetColor[1]) < localTolerance &&
			Math.abs(color[2] - targetColor[2]) < localTolerance
		) {
			return filters = possibleColors[i].filters;
			break;
		}
	}

	localTolerance += tolerance;
	return getFilters(targetColor,localTolerance)
}

function getNewColor(color) {
	var targetColor = color.split(',');
	targetColor = [
	    parseInt(targetColor[0]),// [R]
	    parseInt(targetColor[1]),// [G]
	    parseInt(targetColor[2]),// [B]
    ]
    var filters = getFilters(targetColor,tolerance);
    var filtersCSS = 'filter: ' +
	    'invert('+Math.floor(filters.invert*100)+'%) '+
	    'sepia('+Math.floor(filters.sepia*100)+'%) ' +
	    'saturate('+Math.floor(filters.saturate*100)+'%) ' +
	    'hue-rotate('+Math.floor(filters.hueRotate)+'deg);';
    pixel.style = filtersCSS;
    filtersBox.innerText = filtersCSS
}

getNewColor(color.value);
#pixel {
  width: 50px;
  height: 50px;
  background: rgb(0,0);
}
<input type="text" id="color" placeholder="R,G,B" value="250,150,50" />
<button id="button">get filters</button>
<div id="pixel"></div>
<div id="filters"></div>

编辑:此解决方案不适用于生产用途,仅说明了可以采取的方法来实现OP的要求.原样,它在色谱的某些区域很弱.通过步骤迭代中的更细粒度或通过实现更多过滤器功能可以实现更好的结果,原因在@MultiplyByZer0’s answer中详细描述.

EDIT2:OP正在寻找一种非暴力解决方案.在这种情况下,它非常简单,只需解决这个等式:

哪里

a = hue-rotation
b = saturation
c = sepia
d = invert

猜你在找的CSS相关文章