Можно ли узнать исходный цвет, если известна степень прозрачности имеющегося?
Есть блок некоторого оттенка синего:
ColorPic определяет его код как #4493b1 или rgba(68, 147, 177, 1). Но доподлинно известно, что этот цвет получился в результате придания прозрачности (opacity: 0.7). И при этом данный блок лежит поверх фона у body #dfe2d9.
Можно ли как-то узнать код исходного цвета? Есть ли какая-то формула? Или некий сервис, позволяющий это сделать?
Ответы (1 шт):
Формула (каждый цветовой канал считается отдельно):
XR,G,B = (mixColorR,G,B - knownColorR,G,B × knownColoropacity) ÷ Xopacity
Она получена на базе обычной математики, которой в школе научили. Я не вижу проблемы с уравнением (всего два простых переноса), но если все-таки будут вопросы по формуле - в комментарии. Критика от математиков так же приветствуется :)
Как нетрудно догадаться, эта формула применяется только к аддитивным цветовым моделям. То есть, для излучаемого света (цвета на мониторе) - но не для отражаемого/преломляемого (цвета физических объектов, всякие YRB, CMYK, и т.д.).
Очень простенький калькулятор, только для проверки:
const [bgInp, mixInp, opcInp] = document.querySelectorAll('input'),
outputEl = document.querySelector('#result');
const inputHandler = () => {
// установка цвета контейнеров по значению дочернего инпута
[bgInp, mixInp].forEach(inp => setColor(inp.parentElement, inp.value));
// преобразование значений инпутов в числовые (r, g, b, и их "вес" по opacity)
const bgClr = hexColorToObj(bgInp.value),
mixClr = hexColorToObj(mixInp.value),
mixWeight = Math.min(1, Math.max(0, +opcInp.value || 1)),
bgWeight = 1 - mixWeight;
// вычисление искомого цвета
const result = ['r', 'g', 'b'].reduce((rslt, chnl) => (
rslt += toByteHex((mixClr[chnl] - bgClr[chnl] * bgWeight) / mixWeight)
), '#');
// скрытие/показ элемента с результатом, по наличию ошибок
const hasError = document.querySelector(':invalid') || !/^#[a-f\d]{6}$/.test(result);
document.body.classList.toggle('has-error', hasError);
if (hasError) return;
// вывод результата
[...outputEl.children].forEach((el, idx) => {
setColor(el, !idx ? result : result + toByteHex(255 * mixWeight));
});
};
// добавление обработчика, и его вызов для инициализации
for (const inp of [bgInp, mixInp, opcInp])
inp.addEventListener('input', inputHandler);
inputHandler();
// вспомогательные функции
function setColor(el, clr) {
el.style.background = el.dataset.clr = clr;
}
function hexColorToObj(hexColor) {
const [r, g, b] = hexColor.slice(1).padStart(6, '0').match(/../g).map(v => parseInt(v, 16));
return { r, g, b };
}
function toByteHex(num) {
return Math.round(num).toString(16).padStart(2, '0');
}
* { position: relative; box-sizing: border-box; margin: 0; padding: 0; font: 1.125rem monospace; }
:invalid { border-color: #d44; color: #d44; }
div { width: 32ch; max-width: 100%; margin: 0.5em 0 0; padding: 0.5em; }
input { width: 9ch; padding: 0.2em 0.3em; border: 2px solid transparent; outline: 1px solid #fff; }
#result { display: flex; height: 2.5em; margin: 0; padding: 0; }
#result > span { flex: 1 1; padding: 0.5em; }
#result > span::before {
content: '? ' attr(data-clr);
display: inline;
color: #fff;
mix-blend-mode: difference;
}
#result > :last-child::before { content: 'Проверка'; }
#result > :last-child::after {
content: '◯\a⇅\a◯';
position: absolute;
top: -0.1em; right: 0.3em;
transform: translateY(-50%);
font: 1.2em/1 monospace;
white-space: pre;
opacity: 0.3;
}
.has-error #result { visibility: hidden !important; }
<div>
<input value="#dfe2d9" pattern="#[a-f\d]{6}">
<div>
<input value="#4493b1" pattern="#[a-f\d]{6}">
<input value="0.7" type="number" min="0.01" max="1" step="0.01">
</div>
<div id="result">
<span></span>
<span></span>
</div>
</div>
При итоговых значениях с некорректным форматом, калькулятор просто скрывает результат - то есть, он не исправляет ввод, не конвертирует из коротких трехсимвольных hex'ов и rgba/hsl, и не вычисляет мин. непрозрачность с которой получение смешанного цвета будет возможным.
