เปลี่ยนสีข้อความตามความสว่างของพื้นที่พื้นหลังที่ปกคลุม?


114

ฉันคิดเกี่ยวกับสิ่งต่อไปนี้มาระยะหนึ่งแล้วดังนั้นตอนนี้ฉันต้องการทราบความคิดเห็นของคุณวิธีแก้ปัญหาที่เป็นไปได้และอื่น ๆ

ฉันกำลังมองหาปลั๊กอินหรือเทคนิคที่เปลี่ยนสีของข้อความหรือสลับระหว่างรูปภาพ / ไอคอนที่กำหนดไว้ล่วงหน้าขึ้นอยู่กับความสว่างเฉลี่ยของพิกเซลที่ครอบคลุมของภาพพื้นหลังหรือสีของผู้ปกครอง

หากพื้นที่ปกคลุมของพื้นหลังค่อนข้างมืดให้ทำให้ข้อความเป็นสีขาวหรือเปลี่ยนไอคอน

นอกจากนี้จะเป็นการดีมากหากสคริปต์จะสังเกตเห็นว่าพาเรนต์ไม่มีการกำหนดสีพื้นหลังหรือ - รูปภาพจากนั้นจึงค้นหาสิ่งที่ใกล้ที่สุดต่อไป (จากองค์ประกอบหลักถึงองค์ประกอบหลัก .. )

คุณคิดอย่างไรรู้เกี่ยวกับแนวคิดนี้หรือไม่? มีบางอย่างที่คล้ายกันอยู่แล้วหรือไม่? สคริปต์ตัวอย่าง?

ไชโยเจ.


1
เพียงแค่คิดมากกว่าคำตอบ อาจมีวิธีการตั้งค่าสีของคุณโดยใช้ HSL จากนั้นดูที่ค่าความสว่าง หากค่านั้นสูงกว่าค่าหนึ่งให้ใช้กฎ css
Scott Brown

1
คุณสามารถแยกวิเคราะห์สีพื้นหลังขององค์ประกอบออกเป็นค่า R, G, B (และอัลฟาที่เป็นทางเลือก) ได้โดยใช้โครงสร้าง DOM หากช่องอัลฟาถูกตั้งค่าเป็นศูนย์ อย่างไรก็ตามการพยายามกำหนดสีของภาพพื้นหลังเป็นอีกเรื่องหนึ่งโดยสิ้นเชิง
jackwanders


@Pascal ค่อนข้างคล้ายกันและข้อมูลที่ดี .. แต่มันไม่ใช่คำตอบที่แน่นอนสำหรับคำถามของฉัน
James Cazzetta

คำตอบ:


176

แหล่งข้อมูลที่น่าสนใจสำหรับสิ่งนี้:

นี่คืออัลกอริทึม 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';
}

ขอบคุณสิ่งนี้มีประโยชน์มาก .. ซึ่งขึ้นอยู่กับการตั้งค่าสีพื้นหลัง .. แต่คุณรู้วิธีรับสีเฉลี่ยของภาพโดยการวิ่งผ่านแต่ละพิกเซล (เหมือนวนซ้ำ) หรือไม่?
James Cazzetta

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
อันนี้คือ coo ฉันแค่ปรับ yiq เป็น> = 160 ทำงานได้ดีขึ้นสำหรับฉัน
Arturo

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

1
แล้วเรื่องนี้ล่ะ? stackoverflow.com/questions/2541481/…
jeremyharris

2
ถ้าเป็นสีพื้นหลัง#808080ล่ะ!?
Nathan MacInnes

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) ขึ้นอยู่กับความสว่างของพื้นหลัง

สามารถใช้กับองค์ประกอบที่อยู่นิ่งและเคลื่อนไหวได้

( ที่มา )


ขอบคุณสำหรับการสนับสนุน!
James Cazzetta

สิ่งนี้ใช้ได้กับสีพื้นหลังหรือไม่ ฉันอ่านสคริปต์อย่างรวดเร็วและมองไม่เห็นว่าใช้สีพื้นหลังเพื่อตรวจสอบความสว่าง เฉพาะรูปภาพ
JørgenSkår Fischer

1
สวัสดีJørgenฉันคิดว่าสคริปต์ colourBrightness อาจตอบสนองวัตถุประสงค์ของคุณ: github.com/jamiebrittain/colourBrightness.js
cptstarling

5

หากคุณใช้ ES6 ให้แปลง hex เป็น 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>

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.