色盲模拟器 - 色觉无障碍测试 在线
模拟红色盲、绿色盲和蓝色盲患者看到的颜色。检查WCAG对比度比率,确保无障碍网页设计。
常见问题
什么是色盲模拟器?
色盲模拟器是一种在线工具,显示色觉缺陷(CVD)患者看到的颜色和图像效果。它应用经过科学验证的算法来转换颜色,帮助设计师和开发人员了解大约8%的男性和0.5%的女性色觉异常者如何感知他们的视觉内容。
如何使用这个工具?
此工具有两种模式。颜色模式:使用HEX值、RGB值或颜色选择器输入前景色和背景色,工具会立即显示这些颜色在所有8种色觉缺陷类型下的效果以及WCAG对比度。图像模式:上传图像(拖放或文件浏览),选择CVD类型,即可看到模拟版本与原始图像的并排对比。您还可以下载模拟图像用于文档或演示。
我的数据安全吗?会发送到服务器吗?
您的数据100%安全,永远不会离开您的浏览器。所有颜色计算、WCAG对比度检查和图像处理完全在客户端使用JavaScript和Canvas API执行。没有任何颜色或图像会传输到任何服务器,因此可以安全地测试专有设计、机密品牌资产和敏感的视觉内容。
8种色觉缺陷类型是什么?
8种类型分别是:红色盲(无红色锥体,红色显得暗淡)、绿色盲(无绿色锥体,最常见的二色视觉)、蓝色盲(无蓝色锥体,蓝黄混淆)、红色弱(红色敏感度降低)、绿色弱(绿色敏感度降低,影响约5%男性的最常见CVD)、蓝色弱(蓝色敏感度降低,非常罕见)、全色盲(完全色盲,只能看到灰度)和部分色盲(部分色觉缺失,色彩感知严重降低)。
WCAG对比度检查器如何与色盲模拟配合工作?
工具计算前景色和背景色之间的标准WCAG 2.1对比度,然后在应用每种CVD模拟后重新计算对比度。这可以发现颜色组合在正常视力下通过WCAG标准但对色觉缺陷者来说不合格的情况——这是实现真正无障碍设计的关键洞察。
这个工具使用什么模拟算法?
此工具使用经过同行评审、科学验证的算法:二色视觉类型(红色盲、绿色盲、蓝色盲)使用Brettel, Vienot & Mollon(1997)算法,异常三色视觉类型(红色弱、绿色弱、蓝色弱)使用Machado, Oliveira & Fernandes(2009)算法。全色盲使用ITU-R BT.709亮度加权。为确保精度,所有转换都在线性RGB色彩空间中执行。
我可以用这个工具测试图像吗?
可以。图像选项卡允许您上传最大4096x4096像素的图像(JPG、PNG、WebP、GIF)。工具使用Canvas API应用像素级CVD模拟,并排显示原始图像和模拟图像。您可以下载模拟图像用于无障碍审计或文档编制。
-anopia(色盲)和-anomaly(色弱)类型有什么区别?
后缀"-anopia(色盲)"表示某种锥体类型的完全缺失(二色视觉),而"-anomaly(色弱)"表示敏感度降低(异常三色视觉)。例如,Protanopia(红色盲)意味着完全没有红色锥体,而Protanomaly(红色弱)意味着红色锥体存在但敏感度发生了偏移。色弱类型通常比色盲类型程度更轻且更常见。
代码示例
// Color Blindness Simulator - JavaScript Implementation
/**
* sRGB to Linear RGB conversion
*/
function srgbToLinear(c) {
const v = c / 255;
return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
}
function linearToSrgb(c) {
const v = Math.max(0, Math.min(1, c));
return Math.round(
(v <= 0.0031308 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055) * 255
);
}
/**
* CVD simulation matrices (Brettel 1997 / Machado 2009)
* Each is a 3x3 matrix stored as flat array [row-major]
*/
const CVD_MATRICES = {
protanopia: [
0.152286, 1.052583, -0.204868,
0.114503, 0.786281, 0.099216,
-0.003882, -0.048116, 1.051998,
],
deuteranopia: [
0.367322, 0.860646, -0.227968,
0.280085, 0.672501, 0.047413,
-0.011820, 0.042940, 0.968881,
],
tritanopia: [
1.255528, -0.076749, -0.178779,
-0.078411, 0.930809, 0.147602,
0.004733, 0.691367, 0.303900,
],
achromatopsia: [
0.2126, 0.7152, 0.0722,
0.2126, 0.7152, 0.0722,
0.2126, 0.7152, 0.0722,
],
};
/**
* Simulate color vision deficiency for a single RGB color
* @param {number} r - Red (0-255)
* @param {number} g - Green (0-255)
* @param {number} b - Blue (0-255)
* @param {string} cvdType - CVD type key
* @param {number} severity - 0.0 to 1.0
* @returns {number[]} Simulated [r, g, b]
*/
function simulateColor(r, g, b, cvdType, severity = 1.0) {
const matrix = CVD_MATRICES[cvdType];
if (!matrix) throw new Error(`Unknown CVD type: ${cvdType}`);
// Convert to linear
const lr = srgbToLinear(r);
const lg = srgbToLinear(g);
const lb = srgbToLinear(b);
// Apply matrix
const sr = matrix[0] * lr + matrix[1] * lg + matrix[2] * lb;
const sg = matrix[3] * lr + matrix[4] * lg + matrix[5] * lb;
const sb = matrix[6] * lr + matrix[7] * lg + matrix[8] * lb;
// Lerp with original for severity
const fr = lr * (1 - severity) + sr * severity;
const fg = lg * (1 - severity) + sg * severity;
const fb = lb * (1 - severity) + sb * severity;
return [linearToSrgb(fr), linearToSrgb(fg), linearToSrgb(fb)];
}
/**
* Calculate WCAG 2.1 contrast ratio
*/
function getContrastRatio(fg, bg) {
const lum = (r, g, b) =>
0.2126 * srgbToLinear(r) + 0.7152 * srgbToLinear(g) + 0.0722 * srgbToLinear(b);
const l1 = lum(...fg);
const l2 = lum(...bg);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
/**
* Check WCAG compliance
*/
function checkWCAG(ratio) {
return {
aa: { normalText: ratio >= 4.5, largeText: ratio >= 3 },
aaa: { normalText: ratio >= 7, largeText: ratio >= 4.5 },
};
}
// Usage
const fg = [255, 102, 0]; // Orange
const bg = [255, 255, 255]; // White
console.log("Original contrast:", getContrastRatio(fg, bg).toFixed(2) + ":1");
const cvdTypes = ["protanopia", "deuteranopia", "tritanopia", "achromatopsia"];
for (const type of cvdTypes) {
const simFg = simulateColor(...fg, type);
const simBg = simulateColor(...bg, type);
const ratio = getContrastRatio(simFg, simBg);
const wcag = checkWCAG(ratio);
console.log(`${type}: ${ratio.toFixed(2)}:1 (AA: ${wcag.aa.normalText ? "PASS" : "FAIL"})`);
}