У меня есть несколько логотипов в моих изображениях. Эти логотипы будут импортированы на этот сайт. Каждый логотип имеет случайный цвет (может быть белым, черным, серым, красным, синим, зеленым и т. д.) и имеет прозрачный фон. Например:
Код ниже будет применяться для отображения логотипа на странице сайта:
.magic-box {
width: 200px;
height: 100px;
border: 1px solid black;
position: relative;
border-radius: 20px;
background-color: white; /* can be changed */
}
.magic-image {
max-height: 100%;
max-width: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.images {
display: flex;
}
<div class = "images">
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/b7yHv.png" class = "magic-image" />
</div>
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/IhCH1.png" class = "magic-image" />
</div>
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/tYjdM.png" class = "magic-image" />
</div>
</div>
Проблема в том, что когда я делаю цвет фона .magic-box
белым, белый логотип не отображается. Если для фона установлен черный цвет, черный логотип не виден и т. д.
.magic-box {
width: 200px;
height: 100px;
border: 1px solid black;
position: relative;
border-radius: 20px;
background-color: black; /* can be changed */
}
.magic-image {
max-height: 100%;
max-width: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.images {
display: flex;
}
<div class = "images">
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/b7yHv.png" class = "magic-image" />
</div>
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/IhCH1.png" class = "magic-image" />
</div>
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/tYjdM.png" class = "magic-image" />
</div>
</div>
Как определить наиболее подходящий background-color
для применения к каждому .magic-box
элементу программно?
Примечание. Цвет фона может быть разным для каждого изображения.
@DCR Если у меня розовый логотип, его тоже не видно.
для этого есть несколько встроенных колориметрических инструментов, или вы можете изучить их с помощью книг Йоханнеса Иттена.
@MisterJojo Можно ли применять колориметрию с помощью кода?
эти инструменты сделаны с помощью кода js, так что это возможно.
Вы не сможете сделать это, используя только CSS. Если вам действительно нужно сделать это автоматически, вы можете нарисовать каждый логотип на холсте, чтобы подобрать его цвет, а затем просмотреть все цвета и выбрать тот, который достаточно отличается от всех (конечно, такого не может быть). в теории).
@AHaworth Независимо от того, что это нельзя решить только с помощью CSS, реализация кода JS также принимается. Эти логотипы являются результатом не изображения, а существующего логотипа. Он может содержать текст или значок сплошного цвета.
Учитывая случайный набор логотипов, как вы узнаете, что подходящий фон найден? т.е. какая формула. Вы ищете какой-нибудь цвет, контрастирующий со всеми логотипами одинаково?
@AHaworth Цвет фона / контраст каждого логотипа может быть другим, если это возможно. Как я могу определить цвет фона на основе цвета логотипа?
А, это намного проще. Нарисуйте логотип на холсте, выполните цикл поиска первого непрозрачного байта. Найдите его цвет. Затем сделайте что-нибудь простое, например инвертируйте его, или более сложное посмотрите, как, например, WCAG определяет подходящий контраст.
@AHaworth Логотип — это не результат рисунка, а существующие изображения, такие как это.
Я говорю о рисовании изображения на холсте HTML - я не понимаю, почему это проблема.
О, извините за недоразумение. Можете проиллюстрировать ваш смысл с помощью рабочей скрипки?
Будет ли это ответом на ваш вопрос? stackoverflow.com/questions/1855884/…
Дайте изображению описательное имя, прочитайте имя изображения из атрибута scr
и на его основе установите цвет фона. Или, поскольку логотип — это просто текст, не используйте изображение, просто сделайте это в HTML/Js.
@джорди
Я не думаю, что один цвет фона подходит для всех логотипов, потому что белый — яркий, а серый — темнее, черный — темнее. Вы должны выбрать минимум два или максимум три цвета фона для этих логотипов.
Попробуйте это для выбора цветов. https://coolors.co/contrast-checker
выберите цвет текста для логотипа и цвет фона для фона.
Я думаю, что ваше решение хорошее, но для ручной работы. Я хочу сделать это автоматически.
Если вы уже знаете цвет логотипа, вы можете использовать colord для расчета контраста между логотипом и любым заданным цветом. Затем вы можете зациклить сравнение, регулируя цвет фона, пока не достигнете пригодного для использования значения.
Имейте в виду, что вам нужно будет использовать плагин a11y
для доступа к .contrast()
Что-то вроде:
let contrast = 0;
let backgroundColor = logoColor;
const logoBrightness = colord(logoColor).brightness()
while (contrast < 4.5) {
contrast = colord(logoColor).contrast(backgroundColor);
if (logoBrightness <= 0.5) {
backgroundColor = colord(backgroundColor).lighten(0.05);
} else backgroundColor = colord(backgroundColor).darken(0.05);
}
Вы можете заменить 4.5 на любое желаемое количество контраста. То, как вы примените это значение к элементу backgroundColor
, будет зависеть от ваших фреймворков и предпочтений.
Я знаю, что это может быть простым и, возможно, глупым решением, но добавление drop-shadow
также может решить проблему. Каждый логотип будет виден. Конечно, это зависит от вашего сайта, вписывается ли это в дизайн.
.magic-image {
filter: drop-shadow(0px 0px 7px rgba(10, 12, 15, 0.4));
-webkit-filter: drop-shadow(0px 0px 7px rgba(10, 12, 15, 0.4));
}
@media (prefers-color-scheme: dark) {
.magic-image {
filter: drop-shadow(0px 0px 7px rgba(255, 255, 255, 0.4));
-webkit-filter: drop-shadow(0px 0px 7px rgba(255, 255, 255, 0.4));
}
}
Добавлено к вашему примеру:
.magic-box {
width: 200px;
height: 100px;
border: 1px solid black;
position: relative;
border-radius: 20px;
background-color: white; /* can be changed */
}
.magic-image {
max-height: 100%;
max-width: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
filter: drop-shadow(0px 0px 7px rgba(10, 12, 15, 0.4));
-webkit-filter: drop-shadow(0px 0px 7px rgba(10, 12, 15, 0.4));
}
@media (prefers-color-scheme: dark) {
body,
.magic-box {
background-color: #000;
}
.magic-image {
filter: drop-shadow(0px 0px 12px rgba(255, 255, 255, 0.6));
-webkit-filter: drop-shadow(0px 0px 12px rgba(255, 255, 255, 0.6));
}
}
.images {
display: flex;
}
<div class = "images">
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/b7yHv.png" class = "magic-image" />
</div>
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/IhCH1.png" class = "magic-image" />
</div>
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/tYjdM.png" class = "magic-image" />
</div>
</div>
Именно по этой причине на многих веб-сайтах логотипы отображаются черно-белыми. Является ли это хорошим или плохим решением, зависит от вашего собственного дизайна. В любом случае идея есть.
Уменьшая яркость, вы можете сделать белые части изображения более серыми, так что все ваши изображения будут идеально смотреться на белом фоне. Оттенки серого необязательны, но для более последовательного вида — чтобы не было заметно выбеливания элементов — рекомендую использовать его.
.magic-image {
filter: brightness(50%) grayscale(50%);
-webkit-filter: brightness(50%) grayscale(50%);
}
Добавлено к вашему примеру:
.magic-box {
width: 200px;
height: 100px;
border: 1px solid black;
position: relative;
border-radius: 20px;
background-color: white; /* can be changed */
}
.magic-image {
max-height: 100%;
max-width: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
filter: brightness(50%) grayscale(50%);
-webkit-filter: brightness(50%) grayscale(50%);
}
.images {
display: flex;
}
<div class = "images">
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/b7yHv.png" class = "magic-image" />
</div>
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/IhCH1.png" class = "magic-image" />
</div>
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/tYjdM.png" class = "magic-image" />
</div>
</div>
Этот подход визуализирует изображение размером 1x1 пиксель <canvas>
, чтобы получить среднюю яркость/яркость, чтобы найти подходящий цвет фона.
function adjustBG(image, grayscale = true) {
// try to fix CORS issues
image.crossOrigin = "anonymous";
// draw image on canvas
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
let img = new Image();
img.src = image.src;
img.crossOrigin = "anonymous"; // ADDED
img.onload = function () {
canvas.width = 1;
canvas.height = 1;
ctx.imageSmoothingEnabled = true;
ctx.drawImage(img, 0, 0, 1, 1);
// calculate average color form 1x1 px canvas
let color = ctx.getImageData(0, 0, 1, 1).data;
let [r, g, b, a] = [color[0], color[1], color[2], color[3]];
// calculate relative luminance
let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
let contrast = +((255 - luminance) / 255).toFixed(2);
let bg = [255 - luminance, 255 - luminance, 255 - luminance].map(
(val) => {
return +val.toFixed(0);
}
);
let filters = [];
// optional convert all to grayscale
if (grayscale) {
filters.push(`grayscale(1)`);
}
// add background color if image is very bright
if (luminance > 160 && contrast < 0.5) {
//console.info(bg, luminance)
image.style.backgroundColor = `rgb(${bg.join(",")})`;
} else {
image.style.backgroundColor = `rgb(255,255,255)`;
}
// enhance contrast
if (contrast < 0.5) {
let newContrast = contrast ? 1/contrast : 1;
filters.push(`contrast(${newContrast })`);
}
image.style.filter = filters.join(" ");
};
let images = document.querySelectorAll("img");
addBGColor(true);
function revert() {
images.forEach((img) => {
img.style.removeProperty("background-color");
img.style.removeProperty("filter");
});
}
function addBGColor(grayscale = true) {
images.forEach((img) => {
adjustBG(img, grayscale);
});
}
function adjustBG(image, grayscale = true) {
// try to fix CORS issues
image.crossOrigin = "anonymous";
// draw image on canvas
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
let img = new Image();
img.src = image.src;
img.crossOrigin = "anonymous"; // ADDED
img.onload = function() {
canvas.width = 1;
canvas.height = 1;
ctx.imageSmoothingEnabled = true;
ctx.drawImage(img, 0, 0, 1, 1);
// calculate average color form 1x1 px canvas
let color = ctx.getImageData(0, 0, 1, 1).data;
let [r, g, b, a] = [color[0], color[1], color[2], color[3]];
// calculate relative luminance
let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
let contrast = +((255 - luminance) / 255).toFixed(2);
let bg = [255 - luminance, 255 - luminance, 255 - luminance].map(
(val) => {
return +val.toFixed(0);
}
);
let filters = [];
// optional convert all to grayscale
if (grayscale) {
filters.push(`grayscale(1)`);
}
// add background color if image is very bright
if (luminance > 160 && contrast < 0.5) {
//console.info(bg, luminance)
image.style.backgroundColor = `rgb(${bg.join(",")})`;
} else {
image.style.backgroundColor = `rgb(255,255,255)`;
}
// enhance contrast
if (contrast < 0.5) {
let newContrast = contrast ? 1 / contrast : 1;
filters.push(`contrast(${newContrast })`);
}
image.style.filter = filters.join(" ");
};
}
body {
background: #eee;
}
img {
width: 10em;
margin: 1em;
}
<p><button onclick = "revert()">revert</button> <button onclick = "addBGColor(true)">Add BG color (grayscale)</button> <button onclick = "addBGColor(false)">Add BG color (no change)</button></p>
<div class = "logos">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='20' text-anchor='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 0, 0)'>LO<tspan fill='rgb(128, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.4)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 0, 0)'>LO<tspan fill='rgb(255, 0, 0)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.4)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 255, 0)'>LO<tspan fill='rgb(255, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.5)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 0, 255)'>LO<tspan fill='rgb(128, 128, 128)'>GO</tspan><tspan fill='rgba(0, 255, 255, 0.4)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 255, 0)'>LO<tspan fill='rgb(255, 255, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 1)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 255, 255)'>LO<tspan fill='rgb(128, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.5)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 255, 255)'>LO<tspan fill='rgb(255, 200, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.75)'>*</tspan></text></svg>">
<img src = "https://i.imgur.com/qO6ZdET.png" crossorigin = "anonymous" data-contrast = "0.69" data-bg = "175,175,175" style = "background-color: rgb(175, 175, 175);">
</div>
image.crossOrigin = 'anonymous'
Основываясь на комментарии Рене ван дер Ленде, я изменил скрипт mediumcolor.js и добавил некоторые функции.
Следующий фрагмент также проверяет прозрачность изображения, а также цветовой диапазон только в оттенках серого.
Однако этот скрипт значительно медленнее для более детального определения цвета.
let images = document.querySelectorAll("img");
function revert() {
images.forEach((img) => {
img.style.removeProperty('background-color');
img.style.removeProperty('filter');
});
}
function addBGColor(options = { grayscale: true, complementraryColor: false, enhanceContrast: true }) {
images.forEach((img) => {
adjustBG(img, options);
});
}
function adjustBG(image, options = { grayscale: true, complementraryColor: false, enhanceContrast: true }) {
let grayscale = options.grayscale;
let complementraryColor = options.complementraryColor;
let enhanceContrast = options.enhanceContrast;
// try to fix CORS issues
image.crossOrigin = 'anonymous';
image.addEventListener('load', e=>{
// check transparency
let hasTransparency = checkImgTransparency(image);
let isGray = checkImgGrayScale(image);
// skip opaque images
if (!hasTransparency) {
console.info('nothing to do', image.src);
return false
}
// get average color based on flattened transparency
let { r, g, b, a, colors } = getAverageColor(image, 24, 24, false);
// calculate relative luminance
let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
let contrast = +((255 - luminance) / 255).toFixed(2);
let bg = [255 - luminance, 255 - luminance, 255 - luminance];
// calculate complementary color
let colComplemantary = [255 - r, 255 - g, 255 - b];
let filters = [];
// optional convert all to grayscale
if (grayscale && !isGray) {
filters.push(`grayscale(1)`)
}
// add background color if image is very bright
let contrastAdjust = 1 + (luminance / 255);
let colorBG = !complementraryColor ? 'rgb(255,255,255)' : `rgb(${colComplemantary.join(',')})`;
image.setAttribute('data-contrast', contrast);
image.setAttribute('data-bg', bg.join(','));
// almost white
if (luminance > 170 && contrast < 0.5) {
colorBG = `rgb(${bg.join(',')})`;
}
// enhance contrast
if (enhanceContrast && contrast < 0.5) {
let newContrast = contrast ? 1/contrast : 1;
filters.push(`contrast(${newContrast })`);
}
// apply styles
image.style.backgroundColor = colorBG;
image.style.filter = filters.join(' ');
})
// if image is ready loaded
let isloaded = image.complete;
if (isloaded) {
image.dispatchEvent(new Event('load'));
}
}
/**
* based on
* https://matkl.github.io/average-color/average-color.js
*/
function getAverageColor(img, width=24, height=24, flattenTransparency = false) {
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = true;
canvas.width = width;
canvas.height = height;
//document.body.appendChild(canvas)
// flatten transparency
if (flattenTransparency) {
//add rect
ctx.fillStyle = "rgb(255,255, 255)";
ctx.fillRect(0, 0, width, height);
}
ctx.drawImage(img, 0, 0, width, height);
let imageData = ctx.getImageData(0, 0, width, height);
let data = imageData.data;
let [rT, gT, bT, aT] = [0, 0, 0, 0];
let colLength = data.length/4;
// get colors
let colors = [];
for (let i = 0; i < data.length; i += 4) {
r = data[i];
g = data[i + 1];
b = data[i + 2];
a = data[i + 3];
// exclude transparent colors
if (a>128){
rT += r;
gT += g;
bT += b;
aT += a;
} else{
colLength--;
}
// count colors
let colStr = [r, g, b].join('_');
colors.push(colStr)
}
// calculate average color
rT = Math.floor(rT / colLength);
gT = Math.floor(gT / colLength);
bT = Math.floor(bT / colLength);
aT = Math.floor(aT / colLength);
// remove duplicates
colors = [...new Set(colors)];
return { r: rT, g: gT, b: bT, a: aT , colors: colors.length};
}
function colorIsgrayscale(r, g, b, tolerance = 0.25) {
let isGray = false;
let rT = +(r * tolerance).toFixed(0);
let gT = +(g * tolerance).toFixed(0);
let bT = +(b * tolerance).toFixed(0);
let colorAverage = (rT + gT + bT) / 3;
if (colorAverage == rT && colorAverage == gT && colorAverage == bT) {
isGray = true;
}
return isGray;
}
function checkImgGrayScale(img, tolerance = 0.9) {
let isGrayscale = false;
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = true;
let [w, h] = [8, 8];
ctx.drawImage(img, 0, 0, w, h);
let imageData = ctx.getImageData(0, 0, w, h);
let data = imageData.data;
let gray = 0;
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
let isGray = colorIsgrayscale(r, g, b);
if (isGray){
gray++;
}
}
if (gray===data.length/4){
isGrayscale = true;
}
return isGrayscale;
}
function checkImgTransparency(img) {
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = true;
ctx.drawImage(img, 0, 0, 3, 3);
let imageData = ctx.getImageData(0, 0, 2, 2);
let data = imageData.data;
let hasAlpha = data[3] < 255 ? true : false;
return hasAlpha;
}
body {
background: #eee;
}
img {
width: 10em;
margin: 1em;
}
<p><button onclick = "revert()">revert</button>
<button onclick = "addBGColor({grayscale: true, complementraryColor: false, enhanceContrast: false})">Add BG color (grayscale)</button> <button onclick = "addBGColor({grayscale: false, complementraryColor: true, enhanceContrast: false})">Add BG color (complementary color)</button></p>
<div class = "logos">
<img alt = "logo white" src = "https://i.postimg.cc/FzFQ3n3D/b7yHv.png" class = "magic-image" />
<img alt = "logo black" src = "https://i.postimg.cc/J0TCqcQm/IhCH1.png" class = "magic-image" />
<img src = "https://i.imgur.com/qO6ZdET.png" >
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='20' text-anchor='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 0, 0)'>LO<tspan fill='rgb(128, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.4)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 0, 0)'>LO<tspan fill='rgb(255, 0, 0)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.4)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 255, 0)'>LO<tspan fill='rgb(255, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.5)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 0, 255)'>LO<tspan fill='rgb(128, 128, 128)'>GO</tspan><tspan fill='rgba(0, 255, 255, 0.4)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 255, 0)'>LO<tspan fill='rgb(255, 255, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 1)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 255, 255)'>LO<tspan fill='rgb(128, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.5)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 255, 255)'>LO<tspan fill='rgb(255, 200, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.75)'>*</tspan></text></svg>">
</div>
Подобно первому подходу, мы анализируем контрастность и яркость изображения, визуализируя элемент <canvas>
для вычисления соответствующих значений свойств фильтра для улучшения contrast(2)
или инвертируя очень яркие цвета через invert(1)
let images = document.querySelectorAll("img");
function fixImgColors() {
images.forEach((img) => {
adjustLightColors(img);
});
}
function revert() {
images.forEach((img) => {
img.style.removeProperty('background-color');
img.style.removeProperty('filter');
});
}
function adjustLightColors(image) {
// draw image on canvas
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
let img = new Image();
img.src = image.src;
img.onload = function() {
canvas.width = 1;
canvas.height = 1;
ctx.imageSmoothingEnabled = true;
ctx.drawImage(img, 0, 0, 1, 1);
// calculate average color form 1x1 px canvas
let color = ctx.getImageData(0, 0, 1, 1).data;
let [r, g, b] = [color[0], color[1], color[2]];
// calculate relative luminance
let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
// invert if image is very bright
let filterInvert = luminance > 128 ? 1 : 0;
let contrast = Math.ceil(1 + (luminance / 255));
image.style.filter = `invert(${filterInvert}) grayscale(1) contrast(${contrast}`;
};
}
body {
background: #eee;
}
img {
width: 10em;
margin: 1em;
}
<p><button onclick = "revert()">revert</button> <button onclick = "fixImgColors()">Adjust colors</button></p>
<div class = "logos">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='20' text-anchor='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 0, 0)'>LO<tspan fill='rgb(128, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.4)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 0, 0)'>LO<tspan fill='rgb(255, 0, 0)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.4)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 255, 0)'>LO<tspan fill='rgb(255, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.5)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 0, 255)'>LO<tspan fill='rgb(128, 128, 128)'>GO</tspan><tspan fill='rgba(0, 255, 255, 0.4)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 255, 0)'>LO<tspan fill='rgb(255, 255, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 1)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 255, 255)'>LO<tspan fill='rgb(128, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.5)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 255, 255)'>LO<tspan fill='rgb(255, 200, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.75)'>*</tspan></text></svg>">
</div>
<svg xmlns='http://www.w3.org/2000/svg' width='0' height='0' style = "position:absolute">
<defs>
<filter id='fillBlack' filterUnits='userSpaceOnUse'>
<feFlood flood-color='#000' result='flood' />
<feComposite in='flood' in2='SourceAlpha' operator='in' />
</filter>
</defs>
</svg>
Этот подход основан на SVG-флуд-фильтре
Вставьте скрытый svg в тело HTML:
<svg xmlns='http://www.w3.org/2000/svg' width='0' height='0' style = "position:absolute">
<defs>
<filter id='fillBlack' filterUnits='userSpaceOnUse'>
<feFlood flood-color='#000' result='flood' />
<feComposite in='flood' in2='SourceAlpha' operator='in' />
</filter>
</defs>
</svg>
и сослаться на этот фильтр, определив класс CSS.
.fillBlack {
filter: url('#fillBlack');
}
К сожалению, хром в настоящее время борется с некоторыми фильтрами, когда они встроены в виде URL-адресов данных.
В Firefox вы также можете использовать это:
.fillBlack {
filter: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='fillBlack' filterUnits='userSpaceOnUse'><feFlood flood-color='black' result='flood'/><feComposite in='flood' in2='SourceAlpha' operator='in'/></filter></svg>#fillBlack");
}
let images = document.querySelectorAll("img");
function applySvgFilter() {
images.forEach((img) => {
img.classList.add('fillBlack');
});
}
function revert() {
images.forEach((img) => {
img.classList.remove('fillBlack');
});
}
body {
background: #eee;
}
img {
width: 10em;
margin: 1em;
}
.fillBlack {
filter: url('#fillBlack');
}
<p><button onclick = "revert()">revert</button> <button onclick = "applySvgFilter()">Apply svg filter</button></p>
<div class = "logos">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='20' text-anchor='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 0, 0)'>LO<tspan fill='rgb(128, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.4)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 0, 0)'>LO<tspan fill='rgb(255, 0, 0)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.4)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 255, 0)'>LO<tspan fill='rgb(255, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.5)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 0, 255)'>LO<tspan fill='rgb(128, 128, 128)'>GO</tspan><tspan fill='rgba(0, 255, 255, 0.4)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 255, 0)'>LO<tspan fill='rgb(255, 255, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 1)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(255, 255, 255)'>LO<tspan fill='rgb(128, 128, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.5)'>*</tspan></text></svg>">
<img src = "data:image/svg+xml, <svg width='200' height='100' viewBox='0 0 200 100' xmlns='http://www.w3.org/2000/svg'><text x='100' y='50' font-size='60' dy='5' text-anchor='middle' dominant-baseline='middle' font-family='sans-serif' font-weight='bold' fill='rgb(0, 255, 255)'>LO<tspan fill='rgb(255, 200, 255)'>GO</tspan><tspan fill='rgba(255, 255, 255, 0.75)'>*</tspan></text></svg>">
</div>
<!-- hidden svg filter definition -->
<svg xmlns='http://www.w3.org/2000/svg' width='0' height='0' style = "position:absolute">
<defs>
<filter id='fillBlack' filterUnits='userSpaceOnUse'>
<feFlood flood-color='#000' result='flood' />
<feComposite in='flood' in2='SourceAlpha' operator='in' />
</filter>
</defs>
</svg>
(JavaScript в приведенном выше примере просто помогает проиллюстрировать эффект «до/после»).
Мне нравятся ваши решения, но метод 1 кажется ошибочным. Используя это изображение с <img src = ".." style = "width: 1px"
>, он показывает один белый пиксель, в то время как рассчитанный средний цвет, проходящий через все пиксели, представляет собой rgb (47, 63, 74), очень темный ненасыщенный синий (colorhexa.com). Вероятно, из-за масштабирования HTML по умолчанию от источника 50% 50%. В твоем случае. если центр изображения в основном одноцветный (transparent
выше), то и средний. Не уверен, что происходит, используя метод 1. здесь, на Codepen.
Я использовал This , чтобы получить средний цвет... ( medium-color.js)
Мне нравится первый способ, но он ломает это изображение. Ты можешь починить это?
@Jordy: простите меня - мне потребовалось время, чтобы обнаружить мой глупый недостаток: я пересмотрел фрагмент и добавил более продвинутый сценарий (добавляя новые функции). Основной проблемой предыдущего скрипта была ошибка в расчете контраста (белый был 1, а черный 0 - а должно быть наоборот).
@ Рене ван дер Ленде: большое спасибо за ваш отзыв. Я, наконец, заставил его работать и добавил дополнительный скриптовый подход, основанный на medium-color.js (но я добавил некоторые функции, такие как определение альфа-канала, выравнивание прозрачности и т. д.). Хотя расчет среднего цвета очень неточен, теперь он работает, чтобы решить, какой цвет больше подходит: черный или белый. (Вычисление контраста тоже было неверным) См. также исправленное codepen
Я еще не закончил с этим, не говоря конкретно о вашем решении. Вы заметили, что ОП упоминается в первом абзаце. случайный цвет (может быть белый, черный, серый, красный, синий, зеленый и т.д.) и в параг. после упоминания первого фрагмента. Когда цвет фона установлен на черный, черный логотип не виден и так далее. Это заставило меня предположить, что БГ должен быть любого цвета. Для меня единственным вариантом кажется преобразование цветов в HSL, усреднение их и добавление 180 градусов (чтобы получить дополнительный цвет) и использование результата в качестве цвета BG. Среднее RGB и HSL дают огромную разницу.
Усреднение 3 пикселей чистого красного, зеленого и синего дает rgb(85,85,85)
(очень темно-серый) и hsl(120,100%,50%)
(лаймовый HTML). Два очень разных цвета, как и в случае с HSL, меняется только оттенок. Несмотря на то, что подход HSL с использованием значений imgData
RGB кажется более правильным, он приводит к медленному процессу, когда сначала приходится преобразовывать каждый пиксель в HSL. Как я уже сказал: просто размышляю/воздействую. Я вернусь с демонстрацией, когда закончу. Модифицировал мой SO/a/76023635 с 3-х пиксельным изображением и вашими исправлениями.
Простым ответом может быть использование градиента. Это не идеально, но просто и не требует много кода.
.magic-box {
width: 200px;
height: 100px;
border: 1px solid black;
position: relative;
border-radius: 20px;
background-image: linear-gradient(white, black);
}
.magic-image {
max-height: 100%;
max-width: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.images {
display: flex;
}
<div class = "images">
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/b7yHv.png" class = "magic-image" />
</div>
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/IhCH1.png" class = "magic-image" />
</div>
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/tYjdM.png" class = "magic-image" />
</div>
</div>
Но, конечно, это не выглядит фантастически и немного мягко.
Возможно, что-то вроде этого, которое может принимать ввод цвета и генерировать противоположный цвет, который всегда будет иметь контраст. (Код из этого ТАКОГО вопроса)
Тем не менее, единственное предположение, которое я здесь делаю, заключается в том, что логотипы всегда будут однотонными и что у вас будет возможность вывести HEX-код этого цвета где-нибудь на элементе. В противном случае потребуется написать более сложный код анализа изображений.
function invertColor(hex, bw) {
if (hex.indexOf('#') === 0) {
hex = hex.slice(1);
}
// convert 3-digit hex to 6-digits.
if (hex.length === 3) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
}
if (hex.length !== 6) {
throw new Error('Invalid HEX color.');
}
var r = parseInt(hex.slice(0, 2), 16),
g = parseInt(hex.slice(2, 4), 16),
b = parseInt(hex.slice(4, 6), 16);
if (bw) {
// https://stackoverflow.com/a/3943023/112731
return (r * 0.299 + g * 0.587 + b * 0.114) > 186
? '#000000'
: '#FFFFFF';
}
// invert color components
r = (255 - r).toString(16);
g = (255 - g).toString(16);
b = (255 - b).toString(16);
// pad each with zeros and return
return "#" + padZero(r) + padZero(g) + padZero(b);
}
function padZero(str, len) {
len = len || 2;
var zeros = new Array(len).join('0');
return (zeros + str).slice(-len);
}
//-------
document.querySelectorAll('.magic-box').forEach(box => {
const img = box.querySelector('.magic-image');
const logoColor = img.getAttribute('data-color');
box.style.backgroundColor = invertColor(logoColor, true);
});
.magic-box {
width: 200px;
height: 100px;
border: 1px solid black;
position: relative;
border-radius: 20px;
background: #FFF;
}
.magic-image {
max-height: 100%;
max-width: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.images {
display: flex;
}
<div class = "images">
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/b7yHv.png" class = "magic-image" data-color = "#FFFFFF" />
</div>
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/IhCH1.png" class = "magic-image" data-color = "#000000" />
</div>
<div class = "magic-box">
<img src = "https://i.stack.imgur.com/tYjdM.png" class = "magic-image" data-color = "#808080" />
</div>
</div>
Похоже, пришло время проверить контраст между логотипом и фоном. Вот средство проверки доступности цвета, которое позволяет легче увидеть, когда два цвета достаточно контрастны.
На SO есть несколько других тем, объясняющих, как рассчитать контрастность в javascript: Как программно рассчитать коэффициент контрастности между двумя цветами?
Вы можете получить значения HSL для цветов, а затем отрегулировать яркость фона так, чтобы он находился на самом дальнем расстоянии от яркости логотипа.
то есть: let backgroundLightness = (logoLightness + 50) % 100
если бы у логотипа была яркость 20, фон мог бы быть серым с яркостью 70 hsl(0,0,70)
розовый...........