十六进制颜色的转换(3位,4位,6位),生成色值渐变色,判断颜色值是深色还是浅色

870 阅读2分钟

一、3、4位数字表示的16进制颜色转换成6位

3位转6位

3位数的 每一个数字表示6位数中的两位, 这个补全比较简单,直接举例子:

  • #fff => #ffffff
  • #123 => #112233
  • #a2e => #aa22ee

4位转6位

4位数的前三位和3转6一样,每一个数字表示6位数中的两位,多的第四位表示的是透明度

  • #ffff => #ffffff + 透明度 1 (F/16)
  • #1234 => #112233 + 透明度 0.25 (4/16)
  • #a2e8 => #aa22ee + 透明度 0.5 (8/16)

二、 生成颜色相近的渐变色:

WX20230410-202318.png

通常我们给用户diy的颜色,如果是纯色的话效果不怎么好,需要向深一点或者浅一点进行线性渐变;

export function getColor(color, amount) {
  // 是#开头的十六进制的色值
  if (/^#[0-9A-Fa-f]?/.test(color)) {
    // 如果是4位数的色值,补全成6位,并且取出透明度值,要不计算num会有问题
    const fourReg = /^#[0-9A-Fa-f]{4}$/;
    let alpa = 0;
    if (fourReg.test(color)) {
      const fourLengthReg = /^#([0-9A-Fa-f])([0-9A-Fa-f])([0-9A-Fa-f])([0-9A-Fa-f])/;
      const colorCopy = color;
      color = color.replace(fourLengthReg, `#$1$1$2$2$3$3`);
      alpa = colorCopy.replace(fourLengthReg, `$4`);
    }

    // 如果是3位数的色值,补全成6位,
    const threeReg = /^#[0-9A-Fa-f]{3}$/;
    if (threeReg.test(color)) {
      const testReg = /^#([0-9A-Fa-f])([0-9A-Fa-f])([0-9A-Fa-f])/;
      color = color.replace(testReg, `#$1$1$2$2$3$3`);
    }
    const fill = str => ('00' + str).slice(-2);
    const rgb = getRgb(color.substr(1), amount);
    const newColor =
      '#' + fill(rgb.red.toString(16)) + fill(rgb.green.toString(16)) + fill(rgb.blue.toString(16));
    // 如果颜色有透明度,则生成的渐变色也加上一样的透明度
    if (alpa) {
      return set16ToRgb(newColor, alpa / 16);
    }
    return newColor;
    // 如果色值是 rgba 形式的
  } else if (/rgba\(/.test(color)) {
    return getColor16(color.replace(/;/, ''), amount);
  }
}

// rgba 格式的色值,取渐变色
export function getColor16(rgba, amount) {
  const deleteRegular = /((^rgba\()|(\)))/gi;
  // 去除开头的 'rgba(' 和后面的 ')'
  const newColor = rgba.replace(deleteRegular, '');
  const rgbaList = newColor.split(',');
  const alpa = Number(rgbaList[3]);
  const fill = str => ('00' + str).slice(-2);
  const color16 = `${fill(Number(rgbaList[0]).toString(16))}${fill(
    Number(rgbaList[1]).toString(16)
  )}${fill(Number(rgbaList[2]).toString(16))}`;
  const rgb = getRgb(color16, amount);
  const resolveColor =
    '#' + fill(rgb.red.toString(16)) + fill(rgb.green.toString(16)) + fill(rgb.blue.toString(16));
  if (alpa) {
    const returnColor = set16ToRgb(resolveColor, alpa);
    return returnColor;
  } else {
    const returnColor = set16ToRgb(resolveColor, '0.1');
    return returnColor;
  }
}

function getRgb(color, amount) {
  const clamp = val => Math.min(Math.max(val, 0), 0xff);
  const num = parseInt(color, 16);
  // 用>>不用考虑小于零 或者大于 ffffff 的情况
  const red = clamp((num >> 16) + amount);
  const green = clamp(((num >> 8) & 0x00ff) + amount);
  const blue = clamp((num & 0x0000ff) + amount);
  return {
    red,
    green,
    blue
  };
}

// 给十六进制颜色加上透明度,转换成rgba
export function set16ToRgb(str, alpha) {
  const reg = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;
  if (reg.test(str)) {
    let newStr = str.toLowerCase().replace(/#/g, '');
    const len = newStr.length;
    if (len === 3) {
      let t = '';
      for (let i = 0; i < len; i++) {
        t += newStr.slice(i, i + 1).concat(newStr.slice(i, i + 1));
      }
      newStr = t;
    }
    const arr = [];
    for (let i = 0; i < 6; i = i + 2) {
      const s = newStr.slice(i, i + 2);
      arr.push(parseInt('0x' + s));
    }
    return `rgba(${arr[0]},${arr[1]},${arr[2]},${alpha || '0'})`;
  }
  const rgbaReg = /^(rgba\()(\d+,?){3}/;
  if (rgbaReg.test(str)) {
    const deleteRegular = /((^rgba\()|(\)))/gi;
    const newColor = str.replace(deleteRegular, '');
    const arr = newColor.split(',');
    const alpa = Number(arr[3]);
    return `rgba(${arr[0]},${arr[1]},${arr[2]},${(alpa * alpha).toFixed(3)})`;
  }
}

// 是否浅色
export function isLight(rgb = [152, 152, 152]) {
  return 0.213 * rgb[0] + 0.715 * rgb[1] + 0.072 * rgb[2] > 255 / 2;
}

// 校验颜色是否为浅色
export function checkIsLightColor(color) {
  let rgbaColor = getColor(color, 0);
  const reg = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;
  if (reg.test(rgbaColor)) {
    rgbaColor = set16ToRgb(rgbaColor, 1);
  }
  const deleteRegular = /((^rgba\()|(\)))/gi;
  // 去除开头的 'rgba(' 和后面的 ')'
  const newColor = rgbaColor.replace(deleteRegular, '');
  console.log('newColor newColor', newColor);
  const rgbaList = newColor.split(',');
  return isLight(rgbaList.splice(0, 3));
}

export function colorCheck(value) {
  if (!value) return false;
  // 正则匹配输入的内容是否是带#开头后面,3|4|6个十六进制字符的颜色(0~f)
  const regular = /^#(([0-9A-Fa-f]{3}){1,2}|[0-9A-Fa-f]{4})$/;
  const rgbaRegular = /rgba\(/;
  return regular.test(value) || rgbaRegular.test(value);
}

三、 识别一个色值|图片是深色还是浅色:

因为RGB三种颜色对人眼的刺激程度不同,所以分别对RGB添加不同的权重。得出的平均值来判断色值是深色还是浅色,坚于红光有较长的波长,而绿光波长较短对视觉的刺激相对较小,(红光:625740nm,绿光:波长范围:492~577nm,蓝光:波长范围:440475nm)所以我们要有意的减小红光的权重而提升绿光的权重。

// 色值是否为浅色
export function isLight(rgb = [152, 152, 152]) {
  return 0.213 * rgb[0] + 0.715 * rgb[1] + 0.072 * rgb[2] > 255 / 2;
}