最近在写钉钉小程序,过程中遇到了不少的坑,不过坑后面再总结,今天要讲的是关于十六进制透明度计算。

在这次的设计稿上我发现了这样的 tag 样式:

其实一眼就能看出来实现的思路,背景色其实就是跟着文字的颜色做个淡化。

封装组件的话,主要思路就是:写个 <view>,然后留个 <slot> 插槽用于传入文本,然后写一些样式,最后有个 prop 用于接受一个颜色值,给文字直接设置传入的颜色,然后给 tag 背景色设一个传入颜色一定透明度的色值。我的设计稿中,这个不透明度为 10%。

主要问题是:这个一定透明度的色值怎么计算?

首先,前端的颜色格式有两种,一种是 RGB 格式:rgb(255, 255, 255);另一种是十六进制:#ffffff。

如果给这两种颜色加上透明度,那么 RGB 格式的会变成 rgba(255, 255, 255, 1),这个 a 就是 alpha 通道,用于设置颜色的透明度,1表示完全不透明,0表示完全透明;

十六进制的颜色就会变成:#ffffffff,最后会多出两位,ff表示完全不透明,00表示完全透明。

从上面可以看出多了一个透明度后两种色值的变化:

  1. RGB 格式的颜色从 rgb 变成了 rgba,然后括号里多了一个透明度参数。
  2. 十六进制则是在最后多了两位。

知道区别后,对颜色处理就方便了:

RGB 格式的颜色可以直接用字符串替换或者指定位置插入字符串的方式将 rgb 转为 rgba

十六进制的转换相对来说没有那么直观,但是也并不复杂:

首先我们要指定颜色的不透明度,注意是不透明度,不是透明度,css 中有一个 opacity 属性,就是不透明度的意思,如果你初始为透明度,那么需要先转换成不透明度,这个很简单,如果是0 - 1之间的数值,那么1 - 透明度 = 不透明度,如果是 0 - 100之间的数,那么就是 100 - 透明度 = 不透明度

十六进制的透明度范围并不是 0 - 1 或 0 - 100,而是 00 - ff,用十六进制表示就是 0 - 255,到这里其实就有思路了,将不透明度 * 十进制下的十六进制完全不透明的值,再将结果转成十六进制,拼接到色值最后不就行了吗?

于是就有了下面的函数:

// 此方法只对十六进制色值有效
// color 为十六进制色值
// opacity 需传入0-1之间的数值
function computeColor(color, opacity) {
    // 0 和 1 可以单独进行处理
    // 当然想要函数更健壮还可以对传入的参数进行校验,我这里就不写了
    if(+opacity === 0) return color + '00'
    if(+opacity === 1) return color + 'ff'

    // 转十六进制时必须为数字,所以用 + 转成数字类型
    const num = +Math.floor(255 * opacity);
    // 将十进制转为十六进制
    const ff = num.toString(16);

    // 拼接不透明度至末尾
    return color + ff;
}

另外这里也放上 RGB 与十六进制颜色互转的方法:

// rgb 转 十六进制颜色
function colorHex(color) {
  const that = color;
  //十六进制颜色值的正则表达式
  const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
  // 如果是rgb颜色表示
  if (/^(rgb|RGB)/.test(that)) {
    const aColor = that.replace(/(?:\(|\)|rgb|RGB)*/g, "").split(",");
    let strHex = "#";
    for (let i = 0; i < aColor.length; i++) {
      let hex = Number(aColor[i]).toString(16);
      if (hex.length < 2) {
        hex = "0" + hex;
      }
      strHex += hex;
    }
    if (strHex.length !== 7) {
      strHex = that;
    }
    return strHex;
  } else if (reg.test(that)) {
    const aNum = that.replace(/#/, "").split("");
    if (aNum.length === 6) {
      return that;
    } else if (aNum.length === 3) {
      let numHex = "#";
      for (let i = 0; i < aNum.length; i += 1) {
        numHex += aNum[i] + aNum[i];
      }
      return numHex;
    }
  }
  return that;
}

// 十六进制颜色转 RGB
function colorRgb(color) {
  let sColor = color.toLowerCase();
  //十六进制颜色值的正则表达式
  const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
  // 如果是16进制颜色
  if (sColor && reg.test(sColor)) {
    if (sColor.length === 4) {
      let sColorNew = "#";
      for (let i = 1; i < 4; i += 1) {
        sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
      }
      sColor = sColorNew;
    }
    //处理六位的颜色值
    const sColorChange = [];
    for (let i = 1; i < 7; i += 2) {
      sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2)));
    }
    return "RGB(" + sColorChange.join(",") + ")";
  }
  return sColor;
}