Мой вопрос: учитывая целевой цвет RGB, по какой формуле перекрашивать черный ( #000
) в этот цвет, используя только фильтры CSS ?
Чтобы ответ был принят, необходимо предоставить функцию (на любом языке), которая будет принимать целевой цвет в качестве аргумента и возвращать соответствующую filter
строку CSS .
Контекст для этого - необходимость перекрашивать SVG внутри файла background-image
. В этом случае он предназначен для поддержки определенных математических функций TeX в KaTeX: https://github.com/Khan/KaTeX/issues/587 .
пример
Если целевой цвет #ffff00
(желтый), одно правильное решение:
filter: invert(100%) sepia() saturate(10000%) hue-rotate(0deg)
( демо )
Non-цели
- Анимация.
- Решения без CSS-фильтров.
- Начиная с цвета, отличного от черного.
- Забота о том, что происходит с другими цветами, кроме черного.
Результаты на данный момент
Поиск параметров фиксированного списка фильтров методом перебора: https://stackoverflow.com/a/43959856/181228
Минусы: неэффективно, генерирует только некоторые из 16 777 216 возможных цветов (676 248 сhueRotateStep=1
).Более быстрое решение для поиска с использованием SPSA : https://stackoverflow.com/a/43960991/181228 Награда за вознаграждение
drop-shadow
Решение: https://stackoverflow.com/a/43959853/181228
Минусы: не работает на Крае. Требуютсяfilter
изменения, не связанные с CSS, и незначительные изменения HTML.
Вы по-прежнему можете получить ответ " Принято" , отправив решение без перебора!
Ресурсы
Как
hue-rotate
иsepia
рассчитываются: 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, g, 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
Обратите внимание, что
clamp
вышесказанное делаетhue-rotate
функцию нелинейной.Демо: переход к цвету без оттенков серого из цвета в оттенках серого: https://stackoverflow.com/a/25524145/181228
Формула, которая почти работает (из аналогичного вопроса ):
https://stackoverflow.com/a/29958459/181228Подробное объяснение того, почему приведенная выше формула неверна (CSS
hue-rotate
- это не истинное вращение оттенка, а линейное приближение):
https://stackoverflow.com/a/19325417/2441511
источник
Ответы:
@Dave был первым, кто опубликовал ответ на этот вопрос (с рабочим кодом), и его ответ стал для меня бесценным источником
бессовестной копии ивдохновения. Этот пост начинался как попытка объяснить и уточнить ответ @Dave, но с тех пор превратился в собственный ответ.Мой метод значительно быстрее. Согласно тесту jsPerf на случайно сгенерированных цветах RGB, алгоритм @Dave работает за 600 мс , а мой - за 30 мс . Это определенно может иметь значение, например, во время загрузки, когда скорость имеет решающее значение.
Кроме того, для некоторых цветов мой алгоритм работает лучше:
rgb(0,255,0)
@Dave's производитrgb(29,218,34)
и производитrgb(1,255,0)
rgb(0,0,255)
@Dave's производит,rgb(37,39,255)
а мой -rgb(5,6,255)
rgb(19,11,118)
@Dave's производит,rgb(36,27,102)
а мой -rgb(20,11,112)
Демо
использование
объяснение
Мы начнем с написания Javascript.
Объяснение:
Color
Класс представляет цвет RGB.toString()
функция возвращает цвет вrgb(...)
цветовой строке CSS .hsl()
функция возвращает цвет, преобразованный в HSL .clamp()
функция гарантирует, что заданное значение цвета находится в пределах (0-255).Solver
Класс будет пытаться решить для требуемого цвета.css()
функция возвращает заданный фильтр в строке фильтра CSS.Реализация
grayscale()
,sepia()
иsaturate()
Сердце фильтров CSS / SVG - это примитивы фильтров , которые представляют собой низкоуровневые модификации изображения.
Фильтры
grayscale()
,sepia()
иsaturate()
реализуются примитивом фильтра<feColorMatrix>
, который выполняет умножение матриц между матрицей, заданной фильтром (часто генерируемой динамически), и матрицей, созданной из цвета. Диаграмма:Здесь мы можем сделать некоторые оптимизации:
1
. Нет смысла вычислять или хранить его.A
), поскольку мы имеем дело с RGB, а не с RGBA.<feColorMatrix>
фильтры оставляют столбцы 4 и 5 нулями. Следовательно, мы можем дополнительно уменьшить матрицу фильтра до 3x3 .Реализация:
(Мы используем временные переменные для хранения результатов умножения каждой строки, потому что мы не хотим, чтобы изменения и
this.r
т. Д. Влияли на последующие вычисления.)Теперь, когда мы реализовали
<feColorMatrix>
, мы можем реализоватьgrayscale()
,sepia()
иsaturate()
, которые просто вызывают его с заданной матрицей фильтра:Внедрение
hue-rotate()
hue-rotate()
Фильтр реализуется<feColorMatrix type="hueRotate" />
.Матрица фильтра рассчитывается, как показано ниже:
Например, элемент a 00 будет рассчитываться так:
Некоторые примечания:
Math.sin()
или его необходимо преобразовать в радианыMath.cos()
.Math.sin(angle)
иMath.cos(angle)
должен быть вычислен один раз, а затем кэширован.Реализация:
Реализация
brightness()
иcontrast()
brightness()
Иcontrast()
фильтры реализуются<feComponentTransfer>
с<feFuncX type="linear" />
.Каждый
<feFuncX type="linear" />
элемент принимает атрибут наклона и перехвата . Затем он вычисляет каждое новое значение цвета по простой формуле:Это легко реализовать:
Как только это будет реализовано,
brightness()
а такжеcontrast()
может быть реализовано:Внедрение
invert()
invert()
Фильтр реализован<feComponentTransfer>
с<feFuncX type="table" />
.В спецификации говорится:
Объяснение этой формулы:
invert()
Фильтра определяет эту таблицу: [значение, 1 - значение]. Это tableValues или v .Таким образом, мы можем упростить формулу до:
Подставляя значения таблицы, мы получаем:
Еще одно упрощение:
Спецификация определяет C и C ' как значения RGB в пределах 0-1 (в отличие от 0-255). В результате мы должны масштабировать значения перед вычислением и увеличивать их после.
Итак, мы подошли к нашей реализации:
Интерлюдия: алгоритм грубой силы @Dave
Код @Dave генерирует 176 660 комбинаций фильтров, включая:
invert()
фильтров (0%, 10%, 20%, ..., 100%)sepia()
фильтров (0%, 10%, 20%, ..., 100%)saturate()
фильтров (5%, 10%, 15%, ..., 100%)hue-rotate()
фильтра (0 градусов, 5 градусов, 10 градусов, ..., 360 градусов)Он вычисляет фильтры в следующем порядке:
Затем он перебирает все вычисленные цвета. Он останавливается, когда обнаруживает сгенерированный цвет в пределах допуска (все значения RGB находятся в пределах 5 единиц от целевого цвета).
Однако это медленно и неэффективно. Итак, я даю свой ответ.
Реализация SPSA
Во-первых, мы должны определить функцию потерь , которая возвращает разницу между цветом, полученным с помощью комбинации фильтров, и целевым цветом. Если фильтры идеальны, функция потерь должна вернуть 0.
Мы будем измерять разницу в цвете как сумму двух показателей:
hue-rotate()
, насыщенность коррелирует сsaturate()
и т.д.). Это направляет алгоритм.Функция потерь примет один аргумент - массив процентов фильтров.
Мы будем использовать следующий порядок фильтров:
Реализация:
Мы постараемся минимизировать функцию потерь, чтобы:
SPSA алгоритм ( веб - сайт , подробнее , бумага , бумага реализации , код ссылки ) очень хорош в этом. Он был разработан для оптимизации сложных систем с локальными минимумами, зашумленными / нелинейными / многомерными функциями потерь и т.д. Он использовался для настройки шахматных движков. . И в отличие от многих других алгоритмов, статьи, описывающие его, действительно понятны (хотя и с большим трудом).
Реализация:
Я внес некоторые изменения / оптимизации в SPSA:
deltas
,highArgs
,lowArgs
), вместо того , чтобы воссоздать их с каждой итерации.fix
функции после каждой итерации. Он фиксирует все значения вsaturate
диапазоне от 0% до 100%, кроме (где максимальное значение составляет 7500%),brightness
иcontrast
(где максимальное значение составляет 200%) иhueRotate
(где значения обертываются, а не фиксируются).Я использую SPSA в двухэтапном процессе:
Реализация:
Тюнинг SPSA
Предупреждение: не связывайтесь с кодом SPSA, особенно с его константами, если вы не уверены, что знаете, что делаете.
Важными константами являются A , a , c , начальные значения, пороги повторных попыток, значения
max
infix()
и количество итераций каждого этапа. Все эти значения были тщательно настроены для получения хороших результатов, и случайное их изменение почти наверняка снизит полезность алгоритма.Если вы настаиваете на его изменении, вы должны измерить, прежде чем "оптимизировать".
Сначала примените этот патч .
Затем запустите код в Node.js. Через некоторое время результат должен быть примерно таким:
Теперь настройте константы по своему усмотрению.
Несколько советов:
--debug
флаг, если хотите видеть результат каждой итерации.TL; DR
источник
<filter>
a<feColorMatrix>
с правильными значениями (все нули, кроме последнего столбца, который содержит целевой RGB значения, 0 и 1), вставьте SVG в DOM и укажите фильтр из CSS. Пожалуйста, напишите свое решение в качестве ответа (с демонстрацией), и я проголосую за.Это было настоящее путешествие по кроличьей норе, но вот оно!
РЕДАКТИРОВАТЬ: это решение не предназначено для производственного использования и только иллюстрирует подход, который может быть использован для достижения того, о чем просит OP. Как есть, он слабый в некоторых областях цветового спектра. Лучшие результаты могут быть достигнуты за счет большей детализации в итерациях шага или путем реализации большего количества функций фильтрации по причинам, подробно описанным в ответе @ MultiplyByZer0 .
EDIT2: OP ищет решение без грубой силы. В этом случае это довольно просто, просто решите это уравнение:
где
источник
255,0,255
, мой цифровой измеритель цвета сообщает результат как,#d619d9
а не#ff00ff
.clamp
?Примечание: OP попросил меня отменить удаление , но награда достанется ответу Дейва.
Я знаю, что это не то, что было задано в теле вопроса, и, конечно, не то, чего мы все ждали, но есть один фильтр CSS, который делает именно это:
drop-shadow()
Предостережения:
источник
background-color: black;
в.icon>span
делает эту работу для FF 69b. Однако значок не отображается.Вы можете сделать все очень просто, просто используя фильтр SVG, на который ссылается CSS. Вам нужен только один feColorMatrix для перекраски. Этот меняет цвет на желтый. Пятый столбец в feColorMatrix содержит целевые значения RGB на единичной шкале. (для желтого - 1,1,0)
источник
hue-rotate
в браузерах было бы неплохо, да.url
функцию caniuse.com/#search=svg%20filterЯ заметил, что пример обработки через SVG-фильтр был неполным, я написал свой (который отлично работает): (см. Ответ Майкла Маллани), так что вот способ получить любой цвет, который вы хотите:
Показать фрагмент кода
Вот второе решение, используя SVG-фильтр только в code => URL.createObjectURL
Показать фрагмент кода
источник
просто используйте
fill
Свойство в CSS для заполнения цвета формы SVG.fill
Свойство может принимать любое значение CSS цвета.источник
img
браузером к элементу извне .Я начал с этого ответа, используя фильтр svg, и внес следующие изменения:
SVG-фильтр по URL-адресу данных
Если вы не хотите определять фильтр SVG где-то в разметке, вы можете вместо этого использовать URL-адрес данных (замените R , G , B и A желаемым цветом):
Откат в оттенках серого
Если версия выше не работает, вы также можете добавить откат в оттенках серого.
Функции
saturate
иbrightness
превращают любой цвет в черный (вам не нужно включать это, если цвет уже черный),invert
затем делают его ярче с желаемой яркостью ( L ) и, при необходимости, вы также можете указать непрозрачность ( A ).Миксин SCSS
Если вы хотите указать цвет динамически, вы можете использовать следующий миксин SCSS:
Пример использования:
Преимущества:
hue-rotate
.Предостережения:
источник