# 颜色对比度计算工具技术文档 本模块提供了一组用于计算颜色对比度和评估其是否符合 **WCAG(Web Content Accessibility Guidelines)** 标准的函数。这些函数可用于网页设计、UI 开发等场景中,确保文本与背景之间的可读性和无障碍访问性。 --- ## 目录 - [功能概述](#功能概述) - [函数说明](#函数说明) - [`calculate_luminance(rgba)`](#calculate_luminancergba) - [`get_contrast_ratio(lumA, lumB)`](#get_contrast_ratioluma-lumb) - [`get_color_contrast_ratio(color1, color2)`](#get_color_contrast_ratiocolor1-color2) - [`wcag_check(color1, color2, font_size=14)`](#wcag_checkcolor1-color2-font_size14) - [使用示例](#使用示例) - [注意事项与已知问题](#注意事项与已知问题) - [参考标准](#参考标准) --- ## 功能概述 该模块实现了以下核心功能: 1. 计算给定 RGBA 颜色的相对亮度(Luminance)。 2. 计算两种颜色之间的对比度比值。 3. 判断对比度是否满足 WCAG 2.0 的 AA 或 AAA 级别要求,考虑字体大小的影响。 > ⚠️ 当前代码存在多个拼写错误和变量名错误,实际使用前必须修复! --- ## 函数说明 ### `calculate_luminance(rgba)` **功能:** 计算输入颜色的相对亮度(Relative Luminance),依据 [WCAG 2.0 Suggestion E2](https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-CalcStep) 中定义的加权公式。 **参数:** - `rgba` (`tuple`): 表示颜色的四元组 `(R, G, B, A)`,其中 R、G、B 为 0–255 范围内的整数,A(Alpha)可选(不参与计算)。 > ✅ 注意:此函数仅使用 RGB 分量,忽略 Alpha 通道。 **返回值:** - `float`: 相对亮度值,范围在 `0.0`(黑色)到 `1.0`(白色)之间。 **公式:** ``` L = 0.2126 * R + 0.7152 * G + 0.0722 * B ``` 其中 R、G、B 应先归一化为 0–1 范围(即除以 255),并应用伽马校正(sRGB 转线性亮度)。但当前实现未包含归一化与伽马校正步骤,**这是一个严重缺陷!** > 🔴 **警告:当前实现有误!** > - 变量名错误:`color` 和 `colr` 混用 → 应统一为 `rgba` > - 缺少归一化处理(需将 0–255 映射到 0–1) > - 缺少 sRGB 到线性亮度的转换(非线性响应) ✅ 正确做法应如下: ```python def srgb_to_linear(c): c_norm = c / 255.0 return c_norm / 12.92 if c_norm <= 0.04045 else ((c_norm + 0.055) / 1.055) ** 2.4 lum = 0.2126 * srgb_to_linear(r) + 0.7152 * srgb_to_linear(g) + 0.0722 * srgb_to_linear(b) ``` --- ### `get_contrast_ratio(lumA, lumB)` **功能:** 根据两个颜色的相对亮度计算它们之间的对比度比值。 **参数:** - `lumA`, `lumB` (`float`): 两个颜色的相对亮度值(来自 `calculate_luminance` 输出) **返回值:** - `float`: 对比率,取值范围通常为 `1:1`(无对比)到 `21:1`(最大对比) **公式:** ``` contrast = (lighter + 0.05) / (darker + 0.05) ``` > ⚠️ 错误:函数体内使用了未定义的变量 `lumX`,应为 `lumA` 🔴 存在 Bug: ```python darker = min(lumX, lumB) # ❌ lumX 未定义 ``` ✅ 应改为: ```python darker = min(lumA, lumB) ``` --- ### `get_color_contrast_ratio(color1, color2)` **功能:** 直接传入两个颜色,自动计算其对比度比值。 **参数:** - `color1`, `color2`: 合法的颜色元组 `(R, G, B[, A])` **返回值:** - `float`: 两颜色间的对比度比值 ⚠️ 错误:调用了不存在的函数 `get_contrast_Ratio`(大小写错误) 🔴 原始代码错误: ```python return get_contrast_Ratio(lum1, lum2) # ❌ 函数名拼写错误 ``` ✅ 应为: ```python return get_contrast_ratio(lum1, lum2) ``` --- ### `wcag_check(color1, color2, font_size=14)` **功能:** 判断两个颜色组合是否满足 WCAG 的可访问性标准(AA 和 AAA 级别)。 **参数:** - `color1`, `color2`: 颜色元组 `(R, G, B)` - `font_size` (`int` or `float`): 字体大小(像素),用于决定对比度阈值 **返回值:** - `tuple(bool, bool)`: - 第一个布尔值表示是否达到 **AA 级别** - 第二个布尔值表示是否达到 **AAA 级别** **逻辑规则:** | 字体大小 | AA 标准 | AAA 标准 | |----------------|---------|---------| | ≥ 18px 或粗体≥14px | 3.0 | 4.5 | | < 18px | 4.5 | 7.0 | > ⚠️ 实际 WCAG 规则更复杂,涉及“大字号”定义(18pt 正常 / 14pt 粗体 ≈ 24px),此处简化处理。 🔴 当前代码错误: ```python return ratio >= aa, radio >= aaa # ❌ 'radio' 是拼写错误 ``` ✅ 应为: ```python return ratio >= aa, ratio >= aaa ``` --- ## 使用示例 假设我们想检查白色文字 `(255, 255, 255)` 在深蓝背景 `(0, 0, 128)` 上的可读性: ```python # 示例(需先修复所有 bug 后方可运行) white = (255, 255, 255) blue = (0, 0, 128) aa, aaa = wcag_check(white, blue, font_size=16) print(f"AA compliant: {aa}") # True? print(f"AAA compliant: {aaa}") # False? ``` 预期输出(修复后): ``` AA compliant: True AAA compliant: False ``` --- ## 注意事项与已知问题 📌 **当前代码存在多个致命错误,不能直接使用!** | 问题 | 描述 | 修复建议 | |------|------|----------| | `color` / `colr` / `lumX` 变量名错误 | 引用未定义变量导致运行时异常 | 统一变量名为正确拼写 | | `get_contrast_Ratio` 大小写错误 | Python 区分大小写,函数不存在 | 改为 `get_contrast_ratio` | | `radio` 拼写错误 | 导致 NameError | 改为 `ratio` | | 未归一化 RGB 值 | 输入是 0–255,但公式期望 0–1 | 添加 `/ 255.0` 并应用伽马校正 | | 缺少伽马校正 | 直接线性计算亮度不准确 | 使用 sRGB → 线性转换函数 | | 字体大小判断逻辑不完整 | 未考虑粗体情况 | 建议增加 `bold` 参数 | --- ## 推荐修复版本(完整修正版) ```python def srgb_to_linear(c): """Convert sRGB component to linear light.""" c_norm = c / 255.0 return c_norm / 12.92 if c_norm <= 0.04045 else ((c_norm + 0.055) / 1.055) ** 2.4 def calculate_luminance(rgba): r, g, b = rgba[:3] lr = srgb_to_linear(r) lg = srgb_to_linear(g) lb = srgb_to_linear(b) return 0.2126 * lr + 0.7152 * lg + 0.0722 * lb def get_contrast_ratio(lumA, lumB): lighter = max(lumA, lumB) darker = min(lumA, lumB) return (lighter + 0.05) / (darker + 0.05) def get_color_contrast_ratio(color1, color2): lum1 = calculate_luminance(color1) lum2 = calculate_luminance(color2) return get_contrast_ratio(lum1, lum2) def wcag_check(color1, color2, font_size=14, bold=False): # 简化判断:>=18px 或 (>=14px 且粗体) 视为“大字号” is_large_text = font_size >= 18 or (bold and font_size >= 14) aa_threshold = 3.0 if is_large_text else 4.5 aaa_threshold = 4.5 if is_large_text else 7.0 ratio = get_color_contrast_ratio(color1, color2) return ratio >= aa_threshold, ratio >= aaa_threshold ``` --- ## 参考标准 - [WCAG 2.0 Guideline 1.4.3: Contrast (Minimum)](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html) - [Understanding Success Criterion 1.4.3 | W3C](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html) - [Relative Luminance Calculation - Wikipedia](https://en.wikipedia.org/wiki/Relative_luminance) --- ## 结语 尽管原始代码意图良好,但由于多处语法和逻辑错误,无法正常工作。本文档不仅描述了其设计目标,还指出了关键问题并提供了可运行的修复方案。建议开发者在实际项目中使用经过验证的库(如 `webcolors`、`tinycss2` 或专用 contrast checker 库),或基于本修复版本进行封装。