代码之家  ›  专栏  ›  技术社区  ›  Andrew Leto

使文本根据其下方的颜色动态更改颜色

  •  0
  • Andrew Leto  · 技术社区  · 2 年前

    我正试图让菜单在black&白色取决于下面的颜色。我尝试过混合混合模式,但它会产生某些情况,使文本变得难以辨认。

    我已经设法让它分析背景并输出颜色,但在做出决定时,它似乎只是在阅读自己。我试着避开它(在远离文本之前对a进行分析,但我认为它也不起作用。。。

    问题是: 它可以识别背景,但不会改变文本颜色以与背景形成对比。深色背景=白色文本。,浅色背景=黑色文本我尝试了几种不同的方法,但它们要么会闪烁颜色,要么根本不会改变颜色。。

    function getContrastRatio(color1, color2) {
      // Convert hex colors to RGB if necessary
      color1 = (color1.charAt(0) === '#') ? color1.substr(1) : color1;
      color2 = (color2.charAt(0) === '#') ? color2.substr(1) : color2;
    
      // Extract RGB values
      var r1 = parseInt(color1.substr(0, 2), 16);
      var g1 = parseInt(color1.substr(2, 2), 16);
      var b1 = parseInt(color1.substr(4, 2), 16);
      var r2 = parseInt(color2.substr(0, 2), 16);
      var g2 = parseInt(color2.substr(2, 2), 16);
      var b2 = parseInt(color2.substr(4, 2), 16);
    
      // Calculate relative luminance
      var lum1 = (Math.max(r1, g1, b1) + Math.min(r1, g1, b1)) / 2;
      var lum2 = (Math.max(r2, g2, b2) + Math.min(r2, g2, b2)) / 2;
    
      // Calculate contrast ratio
      var contrastRatio = (lum1 + 0.05) / (lum2 + 0.05);
    
      return contrastRatio;
    }
    
    function readBackgroundColor() {
      var menu = document.querySelector('.sticky-menu');
      var contentTop = menu.offsetTop + menu.offsetHeight;
      
      // Use the body as the default content element
      var content = document.body;
    
      // Iterate over all elements with class "colour" to find the one under the menu
      var colours = document.querySelectorAll('.colour');
      for (var i = 0; i < colours.length; i++) {
        var rect = colours[i].getBoundingClientRect();
        if (rect.top >= contentTop && colours[i] !== menu) { // Exclude the menu from consideration
          break;
        }
        content = colours[i];
      }
    
      // Check if the content element is a child of the menu
      if (!menu.contains(content)) {
        var computedStyle = window.getComputedStyle(content);
        var backgroundColor = computedStyle.backgroundColor;
    
        // Calculate contrast ratio for black and white text
        var blackContrast = getContrastRatio(backgroundColor, 'black');
        var whiteContrast = getContrastRatio(backgroundColor, 'white');
    
        // Choose the color with better contrast
        var textColor = blackContrast > whiteContrast ? 'black' : 'white';
    
        menu.style.color = textColor;
    
        console.log("Background:", backgroundColor, "Text:", textColor);
      }
    }
    
    // Event listener for scroll
    window.addEventListener('scroll', readBackgroundColor);
    
    // Initial call to read background color
    readBackgroundColor();
    body, html {padding:0;margin:0;}
    .colour {height:250px;width:100vw;}
    
    .black {background:black;}
    .blue  {background:blue;}
    .red   {background:red;}
      
    .sticky-menu {
      position: fixed;
      top: 0;
      padding: 10px;
      line-height:0;
      font-size:50px;
    }
    <div class="sticky-menu">home</div>
    
    <div class="colour black"></div>
    <div class="colour white"></div>
    <div class="colour blue"></div>
    <div class="colour red"></div>
    <div class="colour white"></div>
    1 回复  |  直到 2 年前
        1
  •  1
  •   IT goldman    2 年前

    第一个问题是对比度函数的参数。看起来 getComputedStyle 返回中的背景 rgb(25, 25, 25) 总体安排另一个问题是 .white 类未定义为 background: white 所以它没有返回所需的计算背景 rgb(255, 255, 255) 。最后,我拍了 contrast 函数来自 this answer

    最后一个问题,这个例子是这样提供的,你当然可以缩短它。

    编辑:的确切格式 getComputedStyle 背景并没有很好地定义。看见 this answer 这意味着需要进一步的测试来确保格式确实如预期的那样,或者处理所有情况。

    const rgba2hex = (rgba) => `${rgba.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+\.{0,1}\d*))?\)$/).slice(1).map((n, i) => (i === 3 ? Math.round(parseFloat(n) * 255) : parseFloat(n)).toString(16).padStart(2, '0').replace('NaN', '')).join('')}`
    
    const RED = 0.2126;
    const GREEN = 0.7152;
    const BLUE = 0.0722;
    
    const GAMMA = 2.4;
    
    function luminance(r, g, b) {
      var a = [r, g, b].map((v) => {
        v /= 255;
        return v <= 0.03928 ?
          v / 12.92 :
          Math.pow((v + 0.055) / 1.055, GAMMA);
      });
      return a[0] * RED + a[1] * GREEN + a[2] * BLUE;
    }
    
    function contrast(rgb1, rgb2) {
      var lum1 = luminance(...rgb1);
      var lum2 = luminance(...rgb2);
      var brightest = Math.max(lum1, lum2);
      var darkest = Math.min(lum1, lum2);
      return (brightest + 0.05) / (darkest + 0.05);
    }
    
    function getContrastRatio(color1, color2) {
      // Convert hex colors to RGB if necessary
      color1 = rgba2hex(color1)
      color2 = rgba2hex(color2)
    
      // Extract RGB values
      var r1 = parseInt(color1.substr(0, 2), 16);
      var g1 = parseInt(color1.substr(2, 2), 16);
      var b1 = parseInt(color1.substr(4, 2), 16);
      var r2 = parseInt(color2.substr(0, 2), 16);
      var g2 = parseInt(color2.substr(2, 2), 16);
      var b2 = parseInt(color2.substr(4, 2), 16);
    
      return contrast([r1, g1, b1], [r2, g2, b2])
    }
    
    function readBackgroundColor() {
      var menu = document.querySelector('.sticky-menu');
      var contentTop = menu.offsetTop + menu.offsetHeight;
    
      // Use the body as the default content element
      var content = document.body;
    
      // Iterate over all elements with class "colour" to find the one under the menu
      var colours = document.querySelectorAll('.colour');
      for (var i = 0; i < colours.length; i++) {
        var rect = colours[i].getBoundingClientRect();
        if (rect.top >= contentTop && colours[i] !== menu) { // Exclude the menu from consideration
          break;
        }
        content = colours[i];
      }
    
      // Check if the content element is a child of the menu
      if (!menu.contains(content)) {
        var computedStyle = window.getComputedStyle(content);
        var backgroundColor = computedStyle.backgroundColor;
    
        // Calculate contrast ratio for black and white text
        var blackContrast = getContrastRatio(backgroundColor, 'rgb(0,0,0)');
        var whiteContrast = getContrastRatio(backgroundColor, 'rgb(255,255,255)');
    
        // Choose the color with better contrast
        var textColor = blackContrast > whiteContrast ? 'black' : 'white';
    
        menu.style.color = textColor;
    
        // console.log("Background:", backgroundColor, "blackContrast", blackContrast, "whiteContrast", whiteContrast);
      }
    }
    
    // Event listener for scroll
    window.addEventListener('scroll', readBackgroundColor);
    
    // Initial call to read background color
    readBackgroundColor();
    body,
    html {
      padding: 0;
      margin: 0;
    }
    
    .colour {
      height: 250px;
      width: 100vw;
    }
    
    .white {
      background: white;
    }
    
    .black {
      background: black;
    }
    
    .blue {
      background: blue;
    }
    
    .red {
      background: red;
    }
    
    .sticky-menu {
      position: fixed;
      top: 0;
      padding: 10px;
      line-height: 0;
      font-size: 50px;
    }
    <div class="sticky-menu">home</div>
    
    <div class="colour black"></div>
    <div class="colour white"></div>
    <div class="colour blue"></div>
    <div class="colour red"></div>
    <div class="colour white"></div>
    推荐文章