搜尋
首頁web前端css教學釘在光文和背景圖像之間的完美對比度

Nailing the Perfect Contrast Between Light Text and a Background Image

您是否遇到過網站上淺色文本疊加在淺色背景圖片上的情況?如果遇到過,您就會知道這有多難閱讀。避免這種情況的一種常用方法是使用透明疊加層。但這引出一個重要問題:疊加層的透明度究竟應該有多高?我們並非總是處理相同的字體大小、粗細和顏色,當然,不同的圖片也會產生不同的對比度。

嘗試消除背景圖片上文本對比度差的問題,就像玩打地鼠遊戲一樣。與其猜測,不如使用HTML<canvas></canvas>和一些數學方法來解決這個問題。

就像這樣:

我們可以說“問題解決了!”,然後就此結束這篇文章。但這有什麼樂趣呢?我想向您展示的是這個工具的工作原理,以便您掌握一種處理這種常見問題的新方法。

計劃

首先,讓我們明確我們的目標。我們說過,我們想要在背景圖片上顯示可讀的文本,但“可讀”究竟意味著什麼?就我們的目的而言,我們將使用WCAG對AA級可讀性的定義,該定義指出文本和背景顏色之間需要足夠的對比度,以便一種顏色比另一種顏色亮4.5倍。

讓我們選擇一種文本顏色、一張背景圖片和一種疊加顏色作為起點。給定這些輸入,我們想要找到使文本可讀的疊加不透明度級別,而不會使圖片隱藏太多,以至於圖片也難以看到。為了使事情複雜化一點,我們將使用一張既有深色空間又有淺色空間的圖片,並確保疊加層考慮到了這一點。

我們的最終結果將是一個值,我們可以將其應用於疊加層的CSS不透明度屬性,該屬性使文本比背景亮4.5倍。

為了找到最佳疊加不透明度,我們將執行四個步驟:

  1. 我們將圖片放入HTML<canvas></canvas>中,這將使我們能夠讀取圖片中每個像素的顏色。
  2. 我們將找到圖片中與文本對比度最小的像素。
  3. 接下來,我們將準備一個顏色混合公式,我們可以使用它來測試不同不透明度級別對該像素顏色的影響。
  4. 最後,我們將調整疊加層的不透明度,直到文本對比度達到可讀性目標。這不會是隨機猜測——我們將使用二分查找技術來加快此過程。

讓我們開始吧!

步驟1:從畫布讀取圖像顏色

Canvas允許我們“讀取”圖像中包含的顏色。為此,我們需要將圖像“繪製”到<canvas></canvas>元素上,然後使用canvas上下文(ctx)的getImageData()方法生成圖像顏色的列表。

 function getImagePixelColorsUsingCanvas(image, canvas) {
  // canvas的上下文(通常縮寫為ctx)是一個包含許多函數的對象,用於控制你的canvas
  const ctx = canvas.getContext('2d');

  // 寬度可以是任何值,所以我選擇500,因為它足夠大,可以捕捉細節,但又足夠小,可以使計算速度加快。
  canvas.width = 500;

  // 確保canvas與我們圖像的比例匹配canvas.height = (image.height / image.width) * canvas.width;

  // 獲取圖像和canvas的測量值,以便我們可以在下一步中使用它們const sourceImageCoordinates = [0, 0, image.width, image.height];
  const destinationCanvasCoordinates = [0, 0, canvas.width, canvas.height];

  // Canvas的drawImage()的工作原理是將我們圖像的測量值映射到我們想要繪製它的canvas上ctx.drawImage(
    image,
    ...sourceImageCoordinates,
    ...destinationCanvasCoordinates
  );

  // 請記住,getImageData僅適用於相同來源或啟用跨源的圖像。
  // https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image
  const imagePixelColors = ctx.getImageData(...destinationCanvasCoordinates);
  return imagePixelColors;
}

getImageData()方法為我們提供了一個表示每個像素顏色的數字列表。每個像素由四個數字表示:紅色、綠色、藍色和不透明度(也稱為“alpha”)。了解這一點,我們可以遍歷像素列表並找到我們需要的任何信息。這在下一步中將非常有用。

步驟2:找到對比度最小的像素

在此之前,我們需要知道如何計算對比度。我們將編寫一個名為getContrast()的函數,該函數接收兩種顏色並輸出一個數字,表示這兩種顏色之間的對比度級別。數字越高,對比度越好,可讀性也越好。

當我開始研究這個項目的顏色時,我期望找到一個簡單的公式。結果發現有多個步驟。

要計算兩種顏色之間的對比度,我們需要知道它們的亮度級別,這本質上就是亮度(Stacie Arellano對亮度進行了深入探討,值得一看)。

感謝W3C,我們知道使用亮度計算對比度的公式:

 const contrast = (lighterColorLuminance 0.05) / (darkerColorLuminance 0.05);

獲取顏色的亮度意味著我們必須將顏色從網絡上使用的常規8位RGB值(其中每種顏色為0-255)轉換為所謂的線性RGB。我們需要這樣做是因為亮度不會隨著顏色的變化而均勻增加。我們需要將顏色轉換為亮度隨顏色變化均勻變化的格式。這使我們能夠正確計算亮度。同樣,W3C在這裡也提供了幫助:

 const luminance = (0.2126 * getLinearRGB(r) 0.7152 * getLinearRGB(g) 0.0722 * getLinearRGB(b));

但是,等等,還有更多!為了將8位RGB(0到255)轉換為線性RGB,我們需要經歷所謂的標準RGB(也稱為sRGB),其比例為0到1。

因此,過程如下:

 <code>8位RGB → 标准RGB → 线性RGB → 亮度</code>

一旦我們有了想要比較的兩種顏色的亮度,我們就可以將亮度值代入公式中,得到它們各自顏色之間的對比度。

 // getContrast是我們唯一需要直接交互的函數。
// 其餘函數是中間輔助步驟。
function getContrast(color1, color2) {
  const color1_luminance = getLuminance(color1);
  const color2_luminance = getLuminance(color2);
  const lighterColorLuminance = Math.max(color1_luminance, color2_luminance);
  const darkerColorLuminance = Math.min(color1_luminance, color2_luminance);
  const contrast = (lighterColorLuminance 0.05) / (darkerColorLuminance 0.05);
  return contrast;
}

function getLuminance({r,g,b}) {
  return (0.2126 * getLinearRGB(r) 0.7152 * getLinearRGB(g) 0.0722 * getLinearRGB(b));
}
function getLinearRGB(primaryColor_8bit) {
  // 首先從8位rgb(0-255)轉換為標準RGB(0-1)
  const primaryColor_sRGB = convert_8bit_RGB_to_standard_RGB(primaryColor_8bit);

  // 然後從sRGB轉換為線性RGB,以便我們可以使用它來計算亮度const primaryColor_RGB_linear = convert_standard_RGB_to_linear_RGB(primaryColor_sRGB);
  return primaryColor_RGB_linear;
}
function convert_8bit_RGB_to_standard_RGB(primaryColor_8bit) {
  return primaryColor_8bit / 255;
}
function convert_standard_RGB_to_linear_RGB(primaryColor_sRGB) {
  const primaryColor_linear = primaryColor_sRGB <p>現在我們可以計算對比度了,我們需要查看上一步中的圖像,並遍歷每個像素,比較該像素的顏色與前景文本顏色之間的對比度。當我們遍歷圖像的像素時,我們將跟踪到目前為止最差(最低)的對比度,當我們到達循環的末尾時,我們將知道圖像中對比度最差的顏色。</p><pre class="brush:php;toolbar:false"> function getWorstContrastColorInImage(textColor, imagePixelColors) {
  let worstContrastColorInImage;
  let worstContrast = Infinity; // 這保證我們不會從太低的值開始for (let i = 0; i <p></p><h3 id="步驟-準備顏色混合公式來測試疊加不透明度級別">步驟3:準備顏色混合公式來測試疊加不透明度級別</h3><p></p><p>現在我們知道了圖像中對比度最差的顏色,下一步是確定疊加層的透明度應該有多高,並查看這將如何改變與文本的對比度。</p><p></p><p>當我第一次實現這一點時,我使用了單獨的畫布來混合顏色並讀取結果。但是,感謝Ana Tudor關於透明度的文章,我現在知道有一個方便的公式可以計算將基本顏色與透明疊加層混合後的結果顏色。</p><p></p><p>對於每個顏色通道(紅色、綠色和藍色),我們將應用此公式來獲取混合顏色:</p><p>混合顏色= 基本顏色(疊加顏色- 基本顏色) * 疊加不透明度</p><p></p><p>因此,在代碼中,這將如下所示:</p><pre class="brush:php;toolbar:false"> function mixColors(baseColor, overlayColor, overlayOpacity) {
  const mixedColor = {
    r: baseColor.r (overlayColor.r - baseColor.r) * overlayOpacity,
    g: baseColor.g (overlayColor.g - baseColor.g) * overlayOpacity,
    b: baseColor.b (overlayColor.b - baseColor.b) * overlayOpacity,
  }
  return mixedColor;
}

現在我們可以混合顏色了,我們可以測試應用疊加不透明度值時的對比度。

 function getTextContrastWithImagePlusOverlay({textColor, overlayColor, imagePixelColor, overlayOpacity}) {
  const colorOfImagePixelPlusOverlay = mixColors(imagePixelColor, overlayColor, overlayOpacity);
  const contrast = getContrast(textColor, colorOfImagePixelPlusOverlay);
  return contrast;
}

有了這個,我們就擁有了找到最佳疊加不透明度所需的所有工具!

步驟4:找到達到對比度目標的疊加不透明度

我們可以測試疊加層的不透明度,並查看這將如何影響文本和圖像之間的對比度。我們將嘗試許多不同的不透明度級別,直到我們找到達到目標對比度的值,其中文本比背景亮4.5倍。這聽起來可能很瘋狂,但別擔心;我們不會隨機猜測。我們將使用二分查找,這是一個讓我們能夠快速縮小可能的答案集直到得到精確結果的過程。

以下是二分查找的工作原理:

 <code>- 在中间猜测。 - 如果猜测过高,我们将消除答案的上半部分。太低了吗?我们将改为消除下半部分。 - 在新的范围中间猜测。 - 重复此过程,直到我们得到一个值。我碰巧有一个工具可以展示它是如何工作的:在这种情况下,我们试图猜测一个介于0和1之间的不透明度值。因此,我们将从中间猜测,测试结果对比度是太高还是太低,消除一半的选项,然后再次猜测。如果我们将二分查找限制为八次猜测,我们将立即得到一个精确的答案。在我们开始搜索之前,我们需要一种方法来检查是否根本需要叠加层。我们根本不需要优化我们不需要的叠加层! ```javascript function isOverlayNecessary(textColor, worstContrastColorInImage, desiredContrast) { const contrastWithoutOverlay = getContrast(textColor, worstContrastColorInImage); return contrastWithoutOverlay </code>

現在我們可以使用二分查找來查找最佳疊加不透明度:

 function findOptimalOverlayOpacity(textColor, overlayColor, worstContrastColorInImage, desiredContrast) {
  // 如果對比度已經足夠好,我們不需要疊加層,
  // 因此我們可以跳過其餘部分。
  const isOverlayNecessary = isOverlayNecessary(textColor, worstContrastColorInImage, desiredContrast);
  if (!isOverlayNecessary) {
    return 0;
  }

  const opacityGuessRange = {
    lowerBound: 0,
    midpoint: 0.5,
    upperBound: 1,
  };
  let numberOfGuesses = 0;
  const maxGuesses = 8;

  // 如果沒有解決方案,不透明度猜測將接近1,
  // 因此我們可以將其作為上限來檢查無解的情況。
  const opacityLimit = 0.99;

  // 此循環重複縮小我們的猜測,直到我們得到結果while (numberOfGuesses  desiredContrast;

    if (isGuessTooLow) {
      opacityGuessRange.lowerBound = currentGuess;
    }
    else if (isGuessTooHigh) {
      opacityGuessRange.upperBound = currentGuess;
    }

    const newMidpoint = ((opacityGuessRange.upperBound - opacityGuessRange.lowerBound) / 2) opacityGuessRange.lowerBound;
    opacityGuessRange.midpoint = newMidpoint;
  }

  const optimalOpacity = opacityGuessRange.midpoint;
  const hasNoSolution = optimalOpacity > opacityLimit;

  if (hasNoSolution) {
    console.log('No solution'); // 根據需要處理無解的情況return opacityLimit;
  }
  return optimalOpacity;
}

實驗完成後,我們現在確切地知道疊加層的透明度需要多高才能使文本可讀,而不會隱藏過多的背景圖像。

我們做到了!

改進和局限性

我們介紹的方法只有在文本顏色和疊加顏色本身俱有足夠的對比度時才有效。例如,如果您選擇與疊加層相同的文本顏色,除非圖像根本不需要疊加層,否則將不會有最佳解決方案。

此外,即使對比度在數學上是可以接受的,但這並不總是保證它看起來很棒。對於具有淺色疊加層和繁忙背景圖像的深色文本尤其如此。圖像的各個部分可能會分散對文本的注意力,即使對比度在數值上很好,也可能難以閱讀。這就是為什麼流行的建議是在深色背景上使用淺色文本。

我們也沒有考慮像素的位置或每種顏色的像素數量。這樣做的一個缺點是,角落中的像素可能會對結果產生過大的影響。但是,好處是,我們不必擔心圖像的顏色是如何分佈的或文本在哪裡,因為只要我們處理了對比度最少的地方,我們就可以在其他任何地方都安全。

我在此過程中學習到了一些東西

在這個實驗之後,我有一些收穫,我想與你們分享:

 <code>- **明确目标非常有帮助!**我们从一个模糊的目标开始,即想要在图像上显示可读的文本,最终得到了一个我们可以努力达到的特定对比度级别。 - **明确术语非常重要。**例如,标准RGB并非我所期望的。我了解到,我认为的“常规”RGB(0到255)正式称为8位RGB。此外,我认为我研究的方程式中的“L”表示“亮度”,但它实际上表示“亮度”,这不能与“光度”混淆。澄清术语有助于我们编写代码以及讨论最终结果。 - **复杂并不意味着无法解决。**听起来很困难的问题可以分解成更小、更容易管理的部分。 - **当你走过这条路时,你会发现捷径。**对于白色文本在黑色透明叠加层上的常见情况,您永远不需要超过0.54的不透明度即可达到WCAG AA级可读性。 ### 总结…您现在有了一种方法可以在背景图像上使文本可读,而不会牺牲过多的图像。如果您已经读到这里,我希望我已经能够让您大致了解其工作原理。我最初开始这个项目是因为我看到(并制作了)太多网站横幅,其中文本在背景图像上难以阅读,或者背景图像被叠加层过度遮挡。我想做些什么,我想给其他人提供一种同样的方法。我写这篇文章是为了希望你们能够更好地理解网络上的可读性。我希望你们也学习了一些很酷的canvas技巧。如果您在可读性或canvas方面做了一些有趣的事情,我很乐意在评论中听到您的想法!</code>

以上是釘在光文和背景圖像之間的完美對比度的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
什麼是不同的CSS邊框特性?什麼是不同的CSS邊框特性?Apr 28, 2025 pm 05:30 PM

本文討論了CSS邊境屬性,重點是自定義,最佳實踐和響應能力。主要論點:邊境 - 拉迪烏斯(Border-Radius)對響應式設計最有效。

什麼是CSS HSL顏色?什麼是CSS HSL顏色?Apr 28, 2025 pm 05:28 PM

文章討論了CSS HSL顏色,其在網絡設計中的使用以及比RGB的優勢。主要重點是通過直觀的顏色操縱來增強設計和可訪問性。

我們如何在CSS中添加評論?我們如何在CSS中添加評論?Apr 28, 2025 pm 05:27 PM

本文討論了CSS中評論的使用,詳細介紹了單線和多行評論語法。它認為註釋可以增強代碼的可讀性,可維護性和協作,但如果無法正確管理,可能會影響網站性能。

什麼是CSS選擇器?什麼是CSS選擇器?Apr 28, 2025 pm 05:26 PM

本文討論了CSS選擇器,其類型和用於造型HTML元素的用法。它比較ID和類選擇器,並與復雜的選擇器解決性能問題。

哪種類型的CSS持有最高優先級?哪種類型的CSS持有最高優先級?Apr 28, 2025 pm 05:25 PM

本文討論了CSS優先級,重點是具有最高特異性的內聯風格。它解釋了特異性級別,覆蓋方法和用於管理CSS衝突的工具。

CSS的語法是什麼?CSS的語法是什麼?Apr 28, 2025 pm 05:23 PM

文章討論了CSS語法,學習策略,常見錯誤和驗證工具。主要重點是通過實踐和理解來掌握CSS。

建議一些最佳的CSS框架?建議一些最佳的CSS框架?Apr 28, 2025 pm 05:22 PM

文章討論了Bootstrap,Tailwind CSS,Foundation,Bulma,Materizize和Uikit等頂級CSS框架,專注於其功能,用戶友好性,性能,定制和專業用途。

CSS與CSS 3有何不同?CSS與CSS 3有何不同?Apr 28, 2025 pm 05:21 PM

文章討論了從CSS到CSS3的演變,突出了CSS3的新功能,例如動畫和響應式設計工具,以及如何保持與舊瀏覽器的兼容性。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器