Змінити колір тексту на основі яскравості покритої фонової області?


114

Я вже деякий час думав над наступним, тому зараз я хочу знати ваші думки, можливі рішення тощо.

Я шукаю плагін або техніку, яка змінює колір тексту або перемикається між заздалегідь визначеними зображеннями / піктограмами залежно від середньої яскравості покритих пікселів фонового зображення або -кольору батьків.

Якщо накрита область його фону досить темна, зробіть текст білим або перемкніть піктограми.

Крім того, було б чудово, якщо сценарій помітить, якщо у батьків немає визначеного кольору тла або -image, а потім продовжить пошук найбільш близького (від батьківського елемента до батьківського елемента ..).

Як ви думаєте, що знаєте про цю ідею? Чи є вже щось подібне там? сценарії-приклади?

Ура, Дж.


1
Просто думка, а не відповідь. Можливо, ви можете встановити кольори за допомогою HSL, а потім переглянути значення легкості. Якщо це значення вище певного значення, застосуйте правило css.
Скотт Браун

1
Ви могли б розібрати колір тла елемента на значення R, G, B (та необов'язкові альфа), обробляючи дерево DOM, якщо альфа-канал встановлений на нуль. Однак намагатися визначити колір фонового зображення - це зовсім інша справа.
jackwanders


@Pascal Дуже схожий і хороший вклад .. але це не точна відповідь на моє запитання.
James Cazzetta

Відповіді:


175

Цікаві ресурси для цього:

Ось алгоритм W3C (з демо-версією JSFiddle ):

const rgb = [255, 0, 0];

// Randomly change to showcase updates
setInterval(setContrast, 1000);

function setContrast() {
  // Randomly update colours
  rgb[0] = Math.round(Math.random() * 255);
  rgb[1] = Math.round(Math.random() * 255);
  rgb[2] = Math.round(Math.random() * 255);

  // http://www.w3.org/TR/AERT#color-contrast
  const brightness = Math.round(((parseInt(rgb[0]) * 299) +
                      (parseInt(rgb[1]) * 587) +
                      (parseInt(rgb[2]) * 114)) / 1000);
  const textColour = (brightness > 125) ? 'black' : 'white';
  const backgroundColour = 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')';
  $('#bg').css('color', textColour); 
  $('#bg').css('background-color', backgroundColour);
}
#bg {
  width: 200px;
  height: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="bg">Text Example</div>


Фелікс, ти маєш рацію! Зараз я далеко, але впевнений, що коли повернусь, я оновлю відповідь
Alex Ball

1
Можна скоротити таке, якщо ви передаєте йому об’єкт :::: const setContrast = rgb => (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000> 125? 'чорний': 'білий'
Люк Робертсон

Вам потрібен jquery тільки для зміни css?
bluejayke

@bluejayke немає, є й інші способи ;-)
Alex Ball

63

Ця стаття про 24 способи розрахунку контрастності кольорів може вас зацікавити. Ігноруйте перший набір функцій, тому що вони помиляються, але формула YIQ допоможе вам визначити, використовувати світлий або темний колір переднього плану чи ні.

Отримавши колір фону елемента (або предка), ви можете скористатися цією функцією зі статті, щоб визначити відповідний колір переднього плану:

function getContrastYIQ(hexcolor){
    hexcolor = hexcolor.replace("#", "");
    var r = parseInt(hexcolor.substr(0,2),16);
    var g = parseInt(hexcolor.substr(2,2),16);
    var b = parseInt(hexcolor.substr(4,2),16);
    var yiq = ((r*299)+(g*587)+(b*114))/1000;
    return (yiq >= 128) ? 'black' : 'white';
}

Дякую, це дійсно корисно .. Це залежить від заданого кольору фону .. Але чи знаєте ви, як отримати середній колір зображення, пробігаючи кожен піксель (як у циклі)?
Джеймс Казетта

4
У програмі es6 це можна зробити за допомогою:const getContrastYIQ = hc => { const [r, g, b] = [0, 2, 4].map( p => parseInt( hc.substr( p, 2 ), 16 ) ); return ((r * 299) + (g * 587) + (b * 114)) / 1000 >= 128; }
Centril

Я взяв цю функцію і трохи її розширив, щоб ви могли повернути два спеціальні кольори, а не завжди чорний і білий. Зауважте, що якщо кольори два близько один від одного, ви все одно можете отримати контрастні проблеми, але це хороша альтернатива поверненню абсолютних кольорів jsfiddle.net/1905occv/1
Hanna

2
цей крутий, я би просто налаштував yiq на> = 160, працював краще для мене.
Артуро

15

Цікаве запитання. Моя безпосередня думка полягала в тому, щоб перетворити колір тла як текст. Це включає просто розбір фону та інвертування його значення RGB.

Щось подібне: http://jsfiddle.net/2VTnZ/2/

var rgb = $('#test').css('backgroundColor');
var colors = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
var brightness = 1;

var r = colors[1];
var g = colors[2];
var b = colors[3];

var ir = Math.floor((255-r)*brightness);
var ig = Math.floor((255-g)*brightness);
var ib = Math.floor((255-b)*brightness);

$('#test').css('color', 'rgb('+ir+','+ig+','+ib+')');

Ймовірно, ви хочете знежирити свій "перевернутий" колір шляхом усереднення перевернутих значень R, G, B і встановивши їх рівними один одному. Однак це рішення отримує основний колір із рядка, а не з властивості CSS елемента. Для надійності рішення повинно динамічно отримувати кольори фону, які зазвичай повертають значення rgb () або rgba (), але можуть відрізнятися залежно від браузера.
jackwanders

Так. Для зручності розбору я просто використав шістнадцяткове значення. Я оновив скрипку, щоб включити захоплення кольору елемента з CSS. Я оновив скрипку і включив своєрідний контроль яскравості (я не знаю нічого про кольорову математику, тому, мабуть, це не справді яскравість).
jeremyharris


2
Що робити, якщо колір тла #808080!?
Натан Макіннес

1
@NathanMacInnes все одно буде її інвертувати, просто так трапляється, що перевернення чогось прямо в середині спектру призведе до себе. Цей код просто інвертує колір, який відповідає своїм обмеженням.
jeremyharris

9

mix-blend-mode робить трюк:

header {
  overflow: hidden;
  height: 100vh;
  background: url(https://www.w3schools.com/html/pic_mountain.jpg) 50%/cover;
}

h2 {
  color: white;
  font: 900 35vmin/50vh arial;
  text-align: center;
  mix-blend-mode: difference;
  filter: drop-shadow(0.05em 0.05em orange);
}
<header>
  <h2 contentEditable role='textbox' aria-multiline='true' >Edit me here</h2>
</header>

Доповнення (березень 2018 р.): Далі, підручник, що пояснює всі різні типи режимів / реалізацій: https://css-tricks.com/css-techniques-and-effects-for-knockout-text/


5

Я знайшов сценарій BackgroundCheck дуже корисним.

Він визначає надмірну яскравість фону (будь то фонове зображення або колір) і застосовує клас до призначеного текстового елемента ( background--lightабо background--dark), залежно від яскравості фону.

Його можна застосувати до нерухомих і рухомих елементів.

( Джерело )


Дякуємо за внесок!
Джеймс Казетта

Чи працює це для фонових кольорів? Я швидко читаю сценарій, і не бачу його, використовуючи колір тла, щоб перевірити яскравість. Тільки зображення.
Jørgen Skår Fischer

1
Привіт, Jørgen, я думаю, що сценарій colourBrightness може відповідати вашим цілям: github.com/jamiebrittain/colourBrightness.js
cptstarling

5

Якщо ви використовуєте ES6, конвертуйте шістнадцяткову в RGB, тоді ви можете використовувати це:

const hexToRgb = hex => {
    // turn hex val to RGB
    const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
    return result
        ? {
              r: parseInt(result[1], 16),
              g: parseInt(result[2], 16),
              b: parseInt(result[3], 16)
          }
        : null
}

// calc to work out if it will match on black or white better
const setContrast = rgb =>
    (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000 > 125 ? 'black' : 'white'

const getCorrectColor = setContrast(hexToRgb(#ffffff))

4

Ось моя спроба:

(function ($) {
    $.fn.contrastingText = function () {
        var el = this,
            transparent;
        transparent = function (c) {
            var m = c.match(/[0-9]+/g);
            if (m !== null) {
                return !!m[3];
            }
            else return false;
        };
        while (transparent(el.css('background-color'))) {
            el = el.parent();
        }
        var parts = el.css('background-color').match(/[0-9]+/g);
        this.lightBackground = !!Math.round(
            (
                parseInt(parts[0], 10) + // red
                parseInt(parts[1], 10) + // green
                parseInt(parts[2], 10) // blue
            ) / 765 // 255 * 3, so that we avg, then normalize to 1
        );
        if (this.lightBackground) {
            this.css('color', 'black');
        } else {
            this.css('color', 'white');
        }
        return this;
    };
}(jQuery));

Тоді для його використання:

var t = $('#my-el');
t.contrastingText();

Це негайно зробить текст чорним або білим, якщо це доречно. Щоб зробити іконки:

if (t.lightBackground) {
    iconSuffix = 'black';
} else {
    iconSuffix = 'white';
}

Тоді кожна піктограма могла мати вигляд 'save' + iconSuffix + '.jpg'.

Зауважте, що це не буде працювати, коли будь-який контейнер переповнює його батьківський (наприклад, якщо висота CSS дорівнює 0, а переповнення не приховано). Домогтися такої роботи було б набагато складніше.


1

Поєднуючи відповіді [@ alex-ball, @jeremyharris], я вважав, що це найкращий спосіб для мене:

        $('.elzahaby-bg').each(function () {
            var rgb = $(this).css('backgroundColor');
            var colors = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);

            var r = colors[1];
            var g = colors[2];
            var b = colors[3];

            var o = Math.round(((parseInt(r) * 299) + (parseInt(g) * 587) + (parseInt(b) * 114)) /1000);

            if(o > 125) {
                $(this).css('color', 'black');
            }else{
                $(this).css('color', 'white');
            }
        });
*{
    padding: 9px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<div class='elzahaby-bg' style='background-color:#000'>color is white</div>

<div class='elzahaby-bg' style='background-color:#fff'>color is black</div>
<div class='elzahaby-bg' style='background-color:yellow'>color is black</div>
<div class='elzahaby-bg' style='background-color:red'>color is white</div>

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.