如何仅使用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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Css常用的排序方式权重分配 排序方式: 1、按类型&#160;如,显示和浮动、定位、尺寸、字体等 2、按字母&#160;按字母顺序排列,优点是规则简单 3、按定义长度&#160;按照样式定义的字符长度排列 各有优劣,实际应用中,推荐使用第一种。&#160;但是如果单靠前端工程师在编写过程中这么做的
原文:https://www.cnblogs.com/wenruo/p/9732704.html 先上效果 基本是用CSS实现的,没有用图片,加一丢丢JS。不过没有考虑太多兼容性。 首先画一个 &lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;
css属性:word-wrap:break-word; 与 word-break:break-all 的用法; zhangq0123 于 2016-10-19 11:06:12 发布 6475 收藏 9分类专栏: CSS HTML 文章标签: html css版权 CSS同时被 2 个专栏收录8 篇
https://destiny001.gitee.io/color/
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt; &l
css之background的cover和contain的缩放背景图 对于这两个属性,官网是这样解释的: contain 此时会保持图像的纵横比并将图像缩放成将适合背景定位区域的最大大小。 等比例缩放图象到垂直或者水平其中一项填满区域。 cover 此时会保持图像的纵横比并将图像缩放成将完全覆盖背景
.CSS.MAP文件作用 https://blog.csdn.net/qq_36441169/article/details/102575563 1、简介在写前端代码,使用bootstrap时,发现同一个目录下,不仅仅有.css文件的同时,还存在.css.map文件的存在。在前端页面调试时也发现,映
Jquery mobile 写html时文字太长无法自动换行。 Jquery mobile 1 篇文章0 订阅 订阅专栏 加上这个 style=&quot;word-wrap:break-word;word-break:break-all;&quot; 或者 style=&quot;word-wra
详见:http://www.shagua.wiki/project/3 layui图标:http://www.shagua.wiki/project/3?p=85 JQ手册 :https://www.jc2182.com/jquery/jquery-jiaocheng.html css样式手册:ht
css里面圆形的代码,如何使用纯css实现圆形图像或叶子图像?(代码实例) 网易美学于&#160;2021-08-03 22:15:22&#160;发布946&#160;收藏 文章标签:&#160;css里面圆形的代码 有没有想过如何制作那些各式各样的圆形图像而无需用ps,本篇文章就来给你介绍一下如
css文字超出一行就显示省略号 1,css超出一行用点表示 white-space:nowrap; overflow:hidden; text-overflow:ellipsis; 2,css超出二行用点表示 overflow:hidden; text-overflow:ellipsis; disp
js动态追加数据单独设置某一个元素的样式。 在开发时,我们有很多数据是从后台获取然后展示的,例如列表,最近开发碰到个需求是获取到列表信息之后,不仅仅是拼接展示出来,还需要将其中的第一个li元素设置成其他的样式类,在网上找了一堆的办法都和自身业务需求不一致,没办法自己通过chrome控制台一点点调试,
css3手机端h5商品列表页,两列等分排列技巧 .picture_list {&#x9;width: 100%;&#x9;overflow: hidden}.picture_list&gt;li {&#x9;width: 50%; min-height: 120px;&#x9;float: left;&#x9;padding: 0px 3
css3 transform:scale(x)实现字体的缩放: css3 transform:scale(x)字体的缩放: transform:scale(x),针对于整体的缩放,缩放的整体包括宽,高,背景。这自然对于内联元素就无法使用此属性,最好使用无属性的span转换成块元素或者行内块元素进行设
jq获取第一个子元素并添加class &lt;div class=&quot;main&quot;&gt; &lt;div class=&quot;tit&quot;&gt;颜色&lt;/div&gt; &lt;ul&gt; &lt;li&gt;银色&lt;/li&gt; &lt;li&gt;深灰色
设置背景图片的两种方式,并解决手机端背景图片高度自适应问题 赵世婷&#160;2017-09-19 15:59:43 14372 收藏&#160;5 1 设置背景图片的两种方式: 方式一: .back{undefinedposition: fixed;width: 100%;height: 100%
css层级选择器理论{#ek) E:first-child : 匹配的是E元素,E元素是父元素的第一个子元素 说明:利用 :first-child 这个伪类,只有当元素是另一个元素的第一个子元素时才能匹配。例如,p:first-child 会选择作为另外某个元素第一个子元素的所有 p 元素。一般可能
Css多行字符截取方法详解 时间:2021-07-01 10:21:17 相信有很多同学在写前端页面的时候,都会遇到字符长了需要截取的问题,最简单的方法就是手动去截取,可这样又感觉太low了,今天晚上就来讲讲利用css进行字符的截取,不了解css是如何截取的同学可以和我们一起看看哦! 前言 最近在做
css中content可以用到的字符编码 项目中用到的一些特殊字符和图标 html代码 &lt;div class=&quot;cross&quot;&gt;&lt;/div&gt; css代码.cross{width: 20px;height: 20px;border-radius: 10px;b
CSS 计算属性 calc()的完整指南(上) 2020-05-03 CSS tricks上有一系列的完整指南文章,我后面会翻译这些内容,更新不会一下子完成,而是会分成几个,防止自己因看到文章过长而放弃翻译,一步一个脚印。 CSS有一个特殊的calc()函数,用于做基本的数学运算。下面是一个例子: