Ubah warna teks berdasarkan kecerahan area latar belakang yang tercakup?

114

Saya telah memikirkan hal-hal berikut ini untuk sementara waktu, jadi sekarang saya ingin mengetahui pendapat Anda, kemungkinan solusi, dan sebagainya.

Saya mencari plugin atau teknik yang mengubah warna teks atau beralih di antara gambar / ikon yang telah ditentukan sebelumnya tergantung pada kecerahan rata-rata piksel yang tercakup dari gambar latar belakang atau warna induknya.

Jika area tertutup pada latar belakangnya agak gelap, buat teks menjadi putih atau ganti ikon.

Selain itu, akan sangat bagus jika skrip akan memperhatikan jika induk tidak memiliki background-color atau -image yang ditentukan dan kemudian terus mencari yang paling terdekat (dari elemen induk ke elemen induknya ..).

Bagaimana menurut Anda, tahu tentang ide ini? Apakah sudah ada yang serupa di luar sana? contoh skrip?

Cheers, J.

James Cazzetta
sumber
1
Hanya sebuah pikiran, bukan jawaban. Mungkin ada cara untuk mengatur warna Anda menggunakan HSL kemudian melihat nilai kecerahannya. Jika nilai itu di atas nilai tertentu, terapkan aturan css.
Scott Brown
1
Anda bisa mengurai warna latar belakang elemen menjadi nilai R, G, B (dan alfa opsional), mengerjakan pohon DOM jika saluran alfa disetel ke nol. Namun, mencoba menentukan warna gambar latar belakang adalah masalah lain.
jackwanders
@Pascal Cukup mirip, dan masukannya bagus .. tapi itu bukan jawaban yang tepat untuk pertanyaan saya.
James Cazzetta

Jawaban:

175

Sumber daya menarik untuk ini:

Berikut algoritma W3C (dengan demo JSFiddle juga ):

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
sumber
Felix Anda benar! Saya sedang pergi sekarang, tapi tentu, ketika kembali saya akan memperbarui jawabannya
Alex Ball
1
Dapat disingkat menjadi berikut, asalkan Anda mengirimkannya objek :::: const setContrast = rgb => (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000> 125? 'hitam': 'putih'
Luke Robertson
apakah Anda benar-benar membutuhkan jquery hanya untuk mengubah css?
bluejayke
@bluejayke tidak, ada cara lain ;-)
Alex Ball
63

Artikel tentang 24 cara tentang Menghitung Kontras Warna ini mungkin menarik bagi Anda. Abaikan rangkaian fungsi pertama karena salah, tetapi rumus YIQ akan membantu Anda menentukan apakah akan menggunakan warna latar depan terang atau gelap.

Setelah Anda mendapatkan warna latar belakang elemen (atau leluhur), Anda dapat menggunakan fungsi ini dari artikel untuk menentukan warna latar depan yang sesuai:

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';
}
cyang
sumber
Terima kasih, ini sangat membantu .. Ini tergantung pada warna latar belakang yang ditetapkan .. Tetapi apakah Anda tahu cara mendapatkan warna rata-rata dari suatu gambar dengan menjalankan setiap piksel (seperti dalam satu lingkaran)?
James Cazzetta
4
Di es6 Anda dapat melakukan ini dengan: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
Saya mengambil fungsi ini dan mengembangkannya sedikit sehingga Anda bisa mengembalikan dua warna kustom, daripada selalu hitam dan putih. Perhatikan bahwa jika dua warna berdekatan, Anda mungkin masih mendapatkan masalah kontras, tetapi ini adalah alternatif yang baik untuk mengembalikan warna absolut jsfiddle.net/1905occv/1
Hanna
2
yang satu ini keren, saya hanya akan menyesuaikan yiq ke> = 160, bekerja lebih baik untuk saya.
Arturo
15

Pertanyaan menarik. Pikiran langsung saya adalah membalikkan warna latar belakang sebagai teks. Ini hanya melibatkan parsing latar belakang dan membalikkan nilai RGB-nya.

Sesuatu seperti ini: 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+')');
jeremyharris
sumber
Anda mungkin ingin menghilangkan saturasi warna 'terbalik' Anda dengan rata-rata nilai R, G, B terbalik dan mengaturnya sama satu sama lain. Namun, solusi ini mendapatkan warna dasarnya dari string, dan bukan dari properti CSS elemen. Agar dapat diandalkan, solusinya harus mendapatkan warna latar belakang secara dinamis, yang biasanya mengembalikan nilai rgb () atau rgba (), tetapi dapat berbeda menurut browser.
jackwanders
Iya. Untuk kemudahan parsing, saya hanya menggunakan nilai hex. Saya memperbarui biola untuk menyertakan mengambil warna elemen dari CSS. Saya memperbarui biola dan menyertakan semacam kontrol kecerahan (saya tidak tahu apa-apa tentang matematika warna jadi mungkin tidak benar-benar kecerahan).
jeremyharris
1
Bagaimana dengan ini? stackoverflow.com/questions/2541481/…
jeremyharris
2
Bagaimana jika warna latar belakangnya #808080!?
Nathan MacInnes
1
@NathanMacInnes itu masih akan membalikkannya, kebetulan membalikkan sesuatu tepat di tengah spektrum akan menghasilkan dirinya sendiri. Kode ini hanya membalikkan warna, yang datang dengan batasannya.
jeremyharris
9

mix-blend-mode melakukan triknya:

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>

Penambahan (Maret 2018): Mengikuti, tutorial bagus yang menjelaskan semua jenis mode / implementasi yang berbeda: https://css-tricks.com/css-techniques-and-effects-for-knockout-text/

James Cazzetta
sumber
5

Saya telah menemukan skrip BackgroundCheck sangat berguna.

Ini mendeteksi kecerahan latar belakang secara keseluruhan (baik itu gambar latar belakang atau warna), dan menerapkan kelas ke elemen teks yang ditetapkan ( background--lightatau background--dark), tergantung pada kecerahan latar belakang.

Ini dapat diterapkan pada elemen diam dan bergerak.

( Sumber )

cptstarling.dll
sumber
Terima kasih atas kontribusinya!
James Cazzetta
Apakah ini berfungsi untuk warna latar belakang? Saya telah membaca cepat skripnya, dan tidak dapat melihatnya menggunakan warna latar belakang untuk memeriksa kecerahan. Hanya gambar.
Jørgen Skår Fischer
1
Halo Jørgen, saya pikir skrip colourBrightness dapat memenuhi tujuan Anda: github.com/jamiebrittain/colourBrightness.js
cptstarling
5

Jika Anda menggunakan ES6, ubah hex ke RGB kemudian dapat menggunakan ini:

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))
Luke Robertson
sumber
4

Inilah usaha saya:

(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));

Kemudian untuk menggunakannya:

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

Ini akan langsung membuat teks menjadi hitam atau putih sebagaimana mestinya. Untuk melakukan ikon:

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

Kemudian setiap ikon bisa terlihat seperti 'save' + iconSuffix + '.jpg'.

Perhatikan bahwa ini tidak akan berfungsi jika penampung apa pun meluap dari induknya (misalnya, jika tinggi CSS adalah 0, dan luapan tidak disembunyikan). Untuk membuatnya bekerja akan jauh lebih kompleks.

Nathan MacInnes
sumber
1

Dengan menggabungkan jawaban [@ alex-ball, @jeremyharris] menurut saya ini adalah cara terbaik untuk saya:

        $('.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>

A. El-zahaby
sumber