如何根据背景图片来“动态”改变字体颜色?
摘要
“
当背景颜色较“深”的时候,我们通常会使用白色字体;背景颜色较“浅”的时候,使用黑色字体。这是为了在背景不确定的情况下,获得更好的阅读体验。
正文
要实现这种需求,就必须知道背景的“明暗”程度,然后给定一个阈值来判断应该使用“白色”还是“黑色”字体。
这要分两种情况:
- 背景是“颜色”时
- 背景是“图片”时
背景是“颜色”
这种情况相对就比较简单,我们只需要知道当前背景的颜色值,然后计算出颜色的明暗度,就可以了。
假定有一个颜色为#4DB6AC的背景,我们来看具体代码实现:
// #4DB6AC
// 1. 需要先将色值转成 rgb 格式
const rgb = getComputedStyle(bgEl).backgroundColor; // 'rgb(77, 182, 172)'
const [r, g, b] = rgb.match(/\d+(\.\d+)?/g).map(Number);
// 2. 计算背景亮度
const bgL = 0.299 * r + 0.587 * g + 0.114 * b;
// 3. 根据亮度值确定颜色
const textColor = bgL > 128 ? '#000' : '#fff';现在我们逐步进行分析:
1. 转换成 RGB 格式
getComputedStyle是浏览器的API,从DOM上获取到的颜色会自动转成 RGB 格式,然后通过正则表达式分别提取r、g、b三原色的数值。
2. 计算背景亮度
我们需要用到一个公式:
$$Y=0.299R+0.587G+0.114B$$
前面的系数表示每个颜色通道的权重,可以发现它们之和就是1。这些数值是根据ITU-R BT.601-7 标准文档中来的,人眼对三原色的亮度感知大约是:
- 红色:30%左右
- 绿色:59%左右
- 蓝色:11%左右
最后就会得到一个 0~255 之间的数值,值越大表示越亮。
3. 计算对比度确定颜色
然后再根据实际的视觉效果定义一个阈值,比如:128。
当背景亮度大于128时,说明比较亮,我们就使用“黑色”字体,否则就使用“白色”字体。
这里的“黑色”和“白色”表示较暗和较亮的颜色,具体色值根据实际情况而定,阈值
128也是一样。
你也可以根据哪个颜色的亮度与背景颜色亮度差异更大就用哪个。
代码中我们计算出的背景亮度为149.465,大于 128,应该使用黑色字体,效果如下:

从感官上来说,黑色在辨识度上确实要大一些,但我个人其实更偏向于白色字体。所以我们就可以根据实际情况来调整这个阈值,比如设为 150。
背景是“图片”
当背景是图片时,问题就会变得更复杂一些。
我们需要先获取图片的像素点,然后根据像素点的色值来计算图片的平均亮度。
假如有这样一张背景图片:

从人的感官上来说应该使用白色字体会更好一点,我们用代码来计算一下:
// 图片地址 https://cdn.jsdelivr.net/gh/moohng/bucket@oss/images/articles/1757749025610-baby-8797092_640.png
function loadImage() {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.src = 'https://cdn.jsdelivr.net/gh/moohng/bucket@oss/images/articles/1757749025610-baby-8797092_640.png';
img.onload = () => {
// 1. 使用 canvas 绘制图片
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
// 2. 获取图片的像素点
const data = ctx.getImageData(0, 0, img.width, img.height).data;
let r = 0, g = 0, b = 0;
let count = 0;
for (let i = 0, len = data.length; i < len; i += 4 * 100) { // 控制采样频率
r += data[i];
g += data[i + 1];
b += data[i + 2];
count++;
}
// 3. 计算平均亮度
r = r / count;
g = g / count;
b = b / count;
const bgL = r * 0.299 + g * 0.587 + b * 0.114;
// 4. 确定颜色
const textColor = bgL > 128 ? '#000' : '#fff';
};
}1. 加载并绘制图片
首先使用ImageAPI来加载图片,然后将图片绘制到Canvas中。
2. 获取像素点
使用getImageDataAPI可以获取到图片的所有像素点的颜色值,并且每4个一组表示一个像素点,如下所示:

然后通过遍历数组分别对r、g、b三个色值进行累加,并记录累加次数count,用于计算平均值。
3. 计算亮度
这里跟之前一样,先对三原色进行权重计算,从而确定亮度,然后就能够确定字体颜色了。
根据代码计算,最后结果为“白色字体”,这是颜色对比图:

很明显白色字体更符合视觉辨识度。