关于算法:根据背景色自适应文本颜色

5次阅读

共计 2952 个字符,预计需要花费 8 分钟才能阅读完成。

针对企业服务来说,最终用户往往须要更加细化的信息分类形式,而打标签无疑是十分好的解决方案。

如果标签仅仅只提供几种色彩可能无奈满足各个用户的理论需要。那么零碎就须要为用户提供色彩抉择。事实上咱们齐全无奈预知用户抉择了何种色彩,那么如果以后用户抉择了彩色作为背景色,同时以后的字体色彩也是彩色,该标签就无奈应用。如果配置背景色的同时还要求用户配置文字色彩,那么这个标签性能未免有些鸡肋。让用户感觉咱们的开发程度有问题。

所以须要寻找一种解决方案来搞定这个问题。

问题解析

对于黑白转灰度,有一个驰名的公式。咱们能够把十六进制的代码分成 3 个局部,以取得独自的红色,绿色和蓝色的强度。用此算法一一批改像素点的色彩能够将以后的彩色图片变为灰色图像。

gray = r * 0.299 + g * 0.587 + b * 0.114

然而针对亮堂和明朗的色彩,通过公式的计算后肯定会取得不同的数值,而针对以后不同值,咱们取反就能够失去以后的文本色彩。即:

const textColor = (r * 0.299 + g * 0.587 + b * 0.114) > 186 ? '#000' : '#FFF'    

当然了,186 并不是一个确定的数值,你能够依据本人的需要调整一个新的数值。通过该算法, 传入不同的背景色,就能够失去红色和彩色,或者自定义出比拟适合的文本色彩。

欠缺代码

当然,尽管解决的办法非常简单,然而两头还是波及了一些进制转换问题,这里简略传递数值如下所示。

/**
 * @param backgroundColor 字符串 传入  #FFFBBC | FBC | FFBBCC 均可
 */
export function contrastTextColor(backgroundHexColor: string) {
  let hex = backgroundHexColor
  
  // 如果以后传入的参数以 # 结尾, 去除以后的
  if (hex.startsWith('#')) {hex = hex.substring(1);
  }
  // 如果以后传入的是 3 位小数值,间接转换为 6 位进行解决
  if (hex.length === 3) {hex = [hex[0], hex[0], hex[1], hex[1], hex[2], hex[2]].join('')
  }

  if (hex.length !== 6) {throw new Error('Invalid background color.' + backgroundHexColor);
  }

  const r = parseInt(hex.slice(0, 2), 16)
  const g = parseInt(hex.slice(2, 4), 16)
  const b = parseInt(hex.slice(4, 6), 16)
  
  if ([r,g,b].some(x => Number.isNaN(x))) {throw new Error('Invalid background color.' + backgroundHexColor);
  }

  const textColor = (r * 0.299 + g * 0.587 + b * 0.114) > 186 ? '#000' : '#FFF'
  return textColor
}

咱们还能够在其中增加 rgb 色彩,以及转换逻辑。

/**
 * @param backgroundColor 字符串
 */
export function contrastTextColor(backgroundHexColor: string) {// 均转换为 hex 格局,能够传入 rgb(222,33,44)。// 如果以后字符串参数长度大于 7 rgb(,,) 起码为 8 个字符,则认为以后传入的数值为 rgb,进行转换
  const backgroundHexColor = backgroundColor.length > 7 ? convertRGBToHex(backgroundColor) : backgroundColor
  
  // ... 前面代码
}

/** 获取背景色中的多个值, 即 rgb(2,2,2) => [2,2,2] */
const rgbRegex = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/

/** 转换 10 进制为 16 进制, 
  * 计算实现后时字符串后面加 0,同时取后两位数值。使得返回的数值肯定是 两位数
  * 如 E => 0E  |  FF => 0FF => FF
  */
const hex = (x: string) => ("0" + parseInt(x).toString(16)).slice(-2);

function convertRGBToHex(rgb: string): string {const bg = rgb.match(rgbRegex);
  
  if (!bg) {
    // 返回空字符串,在前面判断长度为 6 时候会报错。不在此处进行操作
    return ''
  }
  
  return ("#" + hex(bg[1]) + hex(bg[2]) + hex(bg[3])).toUpperCase();}

当然了,咱们也能够在其中增加缓存代码,以便于缩小计算量。

// 应用 map 来缓存 
const colorByBgColor = new Map()
// 缓存谬误字符串
const CACHE_ERROR = 'error'

export function contrastTextColor(backgroundColor: string) {
  // 获取缓存
  const cacheColor = colorByBgColor.get(backgroundColor)
  if (cacheColor) {
    // 以后缓存谬误,间接报错
    if (cacheColor === CACHE_ERROR) {throw new Error('Invalid background color.' + backgroundColor);
    }
    return colorByBgColor.get(backgroundColor)
  }
  
  // ...
  if (hex.length !== 6) {
    // 间接缓存谬误
    colorByBgColor.set(backgroundColor, CACHE_ERROR)
    throw new Error('Invalid background color.' + backgroundColor);
  }
  
  // ...
  
  if ([r,g,b].some(x => Number.isNaN(x))) {
    // 间接缓存谬误
    colorByBgColor.set(backgroundColor, CACHE_ERROR)
    throw new Error('Invalid background color.' + backgroundColor);
  }

  const textColor = (r * 0.299 + g * 0.587 + b * 0.114) > 186 ? '#000' : '#FFF'
  // 缓存数据
  colorByBgColor.set(backgroundColor, textColor)
  return textColor
}

残缺代码能够在代码库中 转换问题色彩 中看到。

当然了,如果你不须要严格遵循 W3C 准则,以后代码曾经足够应用。然而如果你须要严格遵循你能够参考 http://stackoverflow.com/a/39… 以及 https://www.w3.org/TR/WCAG20/。

激励一下

如果你感觉这篇文章不错,心愿能够给与我一些激励,在我的 github 博客下帮忙 star 一下。
博客地址

参考资料

stackoverflow 问题

正文完
 0