引入
之前想做一个网页色卡,需要背景颜色和文字颜色对比良好。已获得更好视觉效果。这里分享一下如何根据背景颜色自动选择黑色或白色作为文本颜色。
/*
用于填充字符串 `str` 到指定长度 `len`,默认为 2
例如:padZero('F', 2) => '0F' */
function padZero(str, len) {
len = len || 2;
const zeros = new Array(len).join('0');
return (zeros + str).slice(-len);
}
function invertColor(hex, bw) {
if (hex.indexOf('#') === 0) {
hex = hex.slice(1);
}
// convert 3-digit hex to 6-digits.
if (hex.length === 3) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
}
if (hex.length !== 6) {
throw new Error('Invalid HEX color.');
}
let r = parseInt(hex.slice(0, 2), 16),
g = parseInt(hex.slice(2, 4), 16),
b = parseInt(hex.slice(4, 6), 16);
if (bw) {
// https://stackoverflow.com/a/3943023/112731
return (r * 0.299 + g * 0.587 + b * 0.114) > 186
? '#000000'
: '#FFFFFF';
}
// invert color components
r = (255 - r).toString(16);
g = (255 - g).toString(16);
b = (255 - b).toString(16);
// pad each with zeros and return
return "#" + padZero(r) + padZero(g) + padZero(b);
}
通过调用 invertColor
函数,传入背景颜色的 HEX 值,第二个参数为 true
时,返回黑色或白色的 HEX 值。
当第二个参数为 false
时,返回背景颜色的反色 HEX 值。
这里(r * 0.299 + g * 0.587 + b * 0.114) > 186
186 的阈值是基于理论的,但可以根据口味进行调整。
还实现了一个Java类HexColor, 它可以实现合适文本前景色效果。
package cn.stazxr.tools;
import java.io.Serial;
import java.io.Serializable;
import java.util.Objects;
/**
* HexColor class
* A class to represent hex color
* @version 1.0.0
* @since 1.0.0
* @auther stazxr
* @see <a href="https://stazxr.cn">stazxr's blog</a>
*/
public class HexColor implements Serializable {
@Serial
private static final long serialVersionUID = -691002720950767165L;
private String hexColor;
private int r, g, b;
private static final String HEX_REGEX = "(^#|^)([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$";
/**
* Convert 3-digit hex color to 6-digit hex color
*
* @return 6-digit hex color
*/
private String doubleHex() {
return hexColor.substring(0, 1).repeat(2) + hexColor.substring(1, 2).repeat(2) + hexColor.substring(2, 3).repeat(2);
}
/**
* Constructor
*
* @param hexColor 3-digit or 6-digit hex color
*/
public HexColor(String hexColor) {
hexColor = hexColor.replace("#", "").toUpperCase();
if (!hexColor.matches(HEX_REGEX)) {
throw new IllegalArgumentException("Invalid hex color");
}
if (hexColor.length() == 3) {
this.hexColor = doubleHex();
} else {
this.hexColor = hexColor;
}
this.r = Integer.parseInt(this.hexColor.substring(0, 2), 16);
this.g = Integer.parseInt(this.hexColor.substring(2, 4), 16);
this.b = Integer.parseInt(this.hexColor.substring(4, 6), 16);
}
/**
* Constructor
* Default color: #000000
*/
public HexColor() {
hexColor = "000000";
r = 0;
g = 0;
b = 0;
}
/**
* Constructor
*
* @param r red value
* @param g green value
* @param b blue value
*/
public HexColor(int r, int g, int b) {
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
throw new IllegalArgumentException("Invalid RGB value");
}
this.r = r;
this.g = g;
this.b = b;
this.hexColor = padZero(Integer.toHexString(r)) + padZero(Integer.toHexString(g)) + padZero(Integer.toHexString(b));
}
/**
* Pad zero to the left of a string
*
* @param str input string
* @return string with zero padded
*/
private String padZero(String str) {
return String.format("%" + 2 + "s", str).replace(' ', '0').toUpperCase();
}
/**
* Invert color
*
* @param bw whether to return black or white
* @return inverted color
*/
public String invertColor(boolean bw) {
if (this.hexColor == null) {
return null;
}
if (bw) {
int threshold = 186;
int bgDelta = (int) (r * 0.299 + g * 0.587 + b * 0.114);
return (bgDelta > threshold) ? "#000" : "#FFF";
}
return "#" + padZero(Integer.toHexString(255 - r)) + padZero(Integer.toHexString(255 - g)) + padZero(Integer.toHexString(255 - b));
}
/**
* Get red value
*
* @return red value
*/
public String getHexColor() {
return hexColor;
}
/**
* Set hex color
*
* @param hexColor 3-digit or 6-digit hex color
*/
public void setHexColor(String hexColor) {
if (!hexColor.matches(HEX_REGEX)) {
throw new IllegalArgumentException("Invalid hex color");
}
hexColor = hexColor.replace("#", "").toUpperCase();
if (hexColor.length() == 3) {
hexColor = this.doubleHex();
}
this.hexColor = hexColor;
}
/**
* Set red value
*
* @param r red value
* range: 0-255
* @param g green value
* range: 0-255
* @param b blue value
* range: 0-255
*/
public void setHexColor(int r, int g, int b) {
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
throw new IllegalArgumentException("Invalid RGB value");
}
this.r = r;
this.g = g;
this.b = b;
this.hexColor = padZero(Integer.toHexString(r)) + padZero(Integer.toHexString(g)) + padZero(Integer.toHexString(b));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof HexColor)) return false;
return Objects.equals(getHexColor(), ((HexColor) o).getHexColor());
}
@Override
public int hashCode() {
return Objects.hashCode(getHexColor());
}
@Override
public String toString() {
return "#" + hexColor;
}
}
效果如下: