要接受答案,需要提供一个函数(以任何语言)接受目标颜色作为参数并返回相应的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