目錄
計劃
步驟1:從畫布讀取圖像顏色
步驟2:找到對比度最小的像素
步驟3:準備顏色混合公式來測試疊加不透明度級別
步驟4:找到達到對比度目標的疊加不透明度
改進和局限性
我在此過程中學習到了一些東西
首頁 web前端 css教學 釘在光文和背景圖像之間的完美對比度

釘在光文和背景圖像之間的完美對比度

Apr 03, 2025 am 09:44 AM

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

熱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

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

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1669
14
CakePHP 教程
1428
52
Laravel 教程
1329
25
PHP教程
1273
29
C# 教程
1256
24
靜態表單提供商的比較 靜態表單提供商的比較 Apr 16, 2025 am 11:20 AM

讓我們嘗試在這裡造成一個術語:“靜態表單提供商”。你帶上html

使Sass更快的概念證明 使Sass更快的概念證明 Apr 16, 2025 am 10:38 AM

在一個新項目開始時,Sass彙編發生在眼睛的眨眼中。感覺很棒,尤其是當它與browsersync配對時,它重新加載

每周平台新聞:HTML加載屬性,主要的ARIA規格以及從iframe轉移到Shadow dom 每周平台新聞:HTML加載屬性,主要的ARIA規格以及從iframe轉移到Shadow dom Apr 17, 2025 am 10:55 AM

在本週的平台新聞綜述中,Chrome引入了一個用於加載的新屬性,Web開發人員的可訪問性規範以及BBC Move

帶有HTML對話框元素的一些動手 帶有HTML對話框元素的一些動手 Apr 16, 2025 am 11:33 AM

這是我第一次查看HTML元素。我已經意識到了一段時間,但是尚未將其旋轉。它很酷,

紙張形式 紙張形式 Apr 16, 2025 am 11:24 AM

購買或建造是技術的經典辯論。自己構建東西可能會感覺更便宜,因為您的信用卡賬單上沒有訂單項,但是

'訂閱播客”鏈接應在哪裡? '訂閱播客”鏈接應在哪裡? Apr 16, 2025 pm 12:04 PM

有一段時間,iTunes是播客中的大狗,因此,如果您將“訂閱播客”鏈接到喜歡:

託管您自己的非JavaScript分析的選項 託管您自己的非JavaScript分析的選項 Apr 15, 2025 am 11:09 AM

有很多分析平台可幫助您跟踪網站上的訪問者和使用數據。也許最著名的是Google Analytics(廣泛使用)

See all articles