Thay đổi màu văn bản dựa trên độ sáng của vùng nền được bao phủ?


114

Tôi đã nghĩ về những điều sau đây một thời gian, vì vậy bây giờ tôi muốn biết ý kiến ​​của bạn, các giải pháp khả thi, v.v.

Tôi đang tìm kiếm một plugin hoặc kỹ thuật thay đổi màu của văn bản hoặc chuyển đổi giữa các hình ảnh / biểu tượng được xác định trước tùy thuộc vào độ sáng trung bình của các pixel được bao phủ của hình nền hoặc màu gốc của nó.

Nếu khu vực được bao phủ của nền khá tối, hãy đặt văn bản màu trắng hoặc chuyển các biểu tượng.

Ngoài ra, sẽ thật tuyệt nếu tập lệnh thông báo nếu phần tử gốc không có màu nền hoặc -image được xác định và sau đó tiếp tục tìm kiếm phần tử gần nhất (từ phần tử mẹ đến phần tử mẹ ..).

Bạn nghĩ gì, biết gì về ý tưởng này? Có một cái gì đó tương tự như vậy đã có? script-ví dụ?

Chúc mừng, J.


1
Chỉ là một suy nghĩ hơn là một câu trả lời. Có thể có một cách thiết lập màu sắc của bạn bằng cách sử dụng HSL sau đó xem xét giá trị độ đậm nhạt. Nếu giá trị đó cao hơn một giá trị nhất định, hãy áp dụng quy tắc css.
Scott Brown

1
bạn có thể hình dung phân tích cú pháp màu nền của phần tử thành các giá trị R, G, B (và alpha tùy chọn), làm việc với cây DOM nếu kênh alpha được đặt thành 0. Tuy nhiên, cố gắng xác định màu sắc của ảnh nền hoàn toàn là một vấn đề khác.
jackwanders


@Pascal Khá giống và đầu vào tốt .. nhưng nó không phải là câu trả lời chính xác cho câu hỏi của tôi.
James Cazzetta

Câu trả lời:


175

Tài nguyên thú vị cho việc này:

Đây là thuật toán W3C (với cả bản demo 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>


Felix bạn nói đúng! Bây giờ tôi đang đi vắng, nhưng chắc chắn, khi quay lại tôi sẽ cập nhật câu trả lời
Alex Ball

1
Có thể được rút gọn như sau, với điều kiện bạn chuyển cho nó một đối tượng :::: const setContrast = rgb => (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000> 125? 'đen': 'trắng'
Luke Robertson

bạn có cần jquery chỉ để thay đổi css không?
bluejayke

@bluejayke không, có những cách khác ;-)
Alex Ball

63

Bài viết về 24 cách tính độ tương phản màu này có thể bạn quan tâm. Bỏ qua nhóm chức năng đầu tiên vì chúng sai, nhưng công thức YIQ sẽ giúp bạn xác định xem nên sử dụng màu nền trước sáng hay tối.

Sau khi bạn có được màu nền của phần tử (hoặc tổ tiên), bạn có thể sử dụng chức năng này từ bài viết để xác định màu nền trước phù hợp:

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';
}

Cảm ơn, điều này thực sự hữu ích .. Điều này phụ thuộc vào màu nền đã đặt .. Nhưng bạn có biết cách lấy màu trung bình của hình ảnh bằng cách chạy qua từng pixel (như trong một vòng lặp) không?
James Cazzetta

4
Trong es6 bạn có thể làm điều này với: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

Tôi đã sử dụng chức năng này và mở rộng nó một chút để bạn có thể trả về hai màu tùy chỉnh, thay vì luôn là đen và trắng. Lưu ý rằng nếu màu sắc là hai gần nhau bạn có thể vẫn nhận được vấn đề ngược lại, nhưng đây là một lựa chọn tốt để trở về màu sắc tuyệt đối jsfiddle.net/1905occv/1
Hanna

2
cái này thật tuyệt, tôi chỉ cần điều chỉnh yiq thành> = 160, hoạt động tốt hơn cho tôi.
Arturo

15

Câu hỏi thú vị. Ý nghĩ ngay lập tức của tôi là đảo ngược màu của nền làm văn bản. Điều này chỉ liên quan đến việc phân tích cú pháp nền và đảo ngược giá trị RGB của nó.

Một cái gì đó như thế này: 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+')');

Bạn có thể muốn khử bão hòa màu 'đảo ngược' của mình bằng cách lấy trung bình các giá trị R, G, B đảo ngược và đặt chúng bằng nhau. Tuy nhiên, giải pháp này lấy màu cơ bản từ một chuỗi chứ không phải từ thuộc tính CSS của phần tử. Để đáng tin cậy, giải pháp sẽ phải tự động lấy màu nền, thường trả về giá trị rgb () hoặc rgba (), nhưng có thể khác nhau tùy theo trình duyệt.
jackwanders

Đúng. Để dễ phân tích cú pháp, tôi chỉ sử dụng một giá trị hex. Tôi đã cập nhật fiddle để bao gồm lấy màu của phần tử từ CSS. Tôi đã cập nhật fiddle và bao gồm một loại điều khiển độ sáng (tôi không biết gì về toán màu nên có lẽ nó không thực sự độ sáng).
jeremyharris


2
Nếu màu nền thì sao #808080!?
Nathan MacInnes

1
@NathanMacInnes nó sẽ vẫn đảo ngược nó, nó chỉ xảy ra rằng việc đảo ngược thứ gì đó ngay giữa quang phổ sẽ tự nó dẫn đến. Mã này chỉ đảo ngược màu sắc, đi kèm với những hạn chế của nó.
jeremyharris

9

mix-blend-mode thực hiện thủ thuật:

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>

Bổ sung (tháng 3 năm 2018): Sau đây, một hướng dẫn thú vị giải thích tất cả các loại chế độ / cách triển khai khác nhau: https://css-tricks.com/css-techniques-and-effects-for-knockout-text/


5

Tôi đã tìm thấy Kiểm tra Nền tập lệnh rất hữu ích.

Nó phát hiện độ sáng quá mức của nền (có thể là hình nền hoặc màu) và áp dụng một lớp cho phần tử văn bản được chỉ định ( background--lighthoặc background--dark), phụ thuộc vào độ sáng của nền.

Nó có thể được áp dụng cho các yếu tố tĩnh và chuyển động.

( Nguồn )


Cảm ơn vì sự đóng góp!
James Cazzetta

Điều này có phù hợp với màu nền không? Tôi đã đọc nhanh tập lệnh và không thể thấy nó sử dụng màu nền để kiểm tra độ sáng. Chỉ hình ảnh.
Jørgen Skår Fischer

1
Xin chào Jørgen, tôi nghĩ rằng kịch bản colourBrightness có thể phục vụ mục đích của bạn: github.com/jamiebrittain/colourBrightness.js
cptstarling

5

Nếu bạn đang sử dụng ES6, hãy chuyển đổi hex sang RGB sau đó có thể sử dụng:

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

Đây là nỗ lực của tôi:

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

Sau đó, để sử dụng nó:

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

Điều này ngay lập tức sẽ làm cho văn bản có màu đen hoặc trắng nếu thích hợp. Để thực hiện các biểu tượng:

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

Sau đó, mỗi biểu tượng có thể trông như thế nào 'save' + iconSuffix + '.jpg'.

Lưu ý rằng điều này sẽ không hoạt động khi bất kỳ vùng chứa nào tràn phần gốc của nó (ví dụ: nếu chiều cao CSS là 0 và phần tràn không bị ẩn). Để làm được điều đó sẽ phức tạp hơn rất nhiều.


1

Bằng cách kết hợp các câu trả lời [@ alex-ball, @jeremyharris], tôi thấy đây là cách tốt nhất cho mình:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.