头图

本篇文章主要分两部分来讲解,首先带你进一步认识一下颜色模型相关的概念,然后在此基础上封装一个自己的ColorPicker组件。

同时,在文章结尾处将分享一个前端小技巧,不要错过哦!

颜色模型

我们平时开发中,经常用到的颜色模型主要是十六进制RGB两种,浏览器所支持的颜色模型还有颜色标识符HSL。作为前端开发人员,基本只需要根据设计稿给出的色值,复制过来就可以了,但是如果要封装一个颜色选择组件,却不得不了解一下这几种颜色模型的原理。

RGB和十六进制

其中最直观的颜色模型应该就是RGB了,这个比较简单,它主要是将颜色分为了3个通道redgreenblue,就分别对应RGB。然后每一个通道又分成了256(0~255)个级别,来表示颜色的强度。根据屏幕的三原色原理,就可以合成我们需要的各种颜色了,它所能表示的颜色就有256 ** 3 = 16777216种,已经远远超过人眼所能识别的颜色范围了。

十六进制也是属于RGB颜色模型,只是展示的形式不一样而已。它将RGB所表示的十进制数值转成十六进制来表示,每个颜色通道(0~255)十六进制就是(00~FF),并也#号开头。

举例:

  1. 所有颜色通道为0时,表现出来的颜色就是全黑,RGB表示rgb(0,0,0),十六进制表示#000000或者#000
  2. 所有颜色通道为255时,表现出来的就是全白,RGB表示rgb(255,255,255),十六进制表示#ffffff或者#fff;
  3. 同理,蓝色、绿色、红色就是各自通道的数值设置最大,其他通道的数值设置最小。

虽然RGB三通道模型很直观,也能表示那么多的颜色,但是很显然,很难通过人来调出自己想要的颜色。因为当我们需要一个颜色时,很难人为把控哪个通道的颜色需要多一点,哪个颜色通道设置小一些。专业人士可能会根据经验大概能调出来,但是对于我们的用户小白,基本上就是束手无策了。

HSL和HSV(很关键)

针对上述问题,我们就不得不了解一下其他两个颜色模型了:HSLHSV。 这两个颜色模型很相似,所以放在一起说了,搞明白了这两个颜色显示原理,就可以玩出很多花样了。

HSL主要是将颜色分为了 色相(H:hue)、饱和度(S:saturation)、亮度(L:lightness) 三个通道,其中最重要的就是色相(H)了。

色相H就是基于红、绿、蓝三原色用一个360°的圆环来表示,红色位于0°,绿色位于120°,蓝色位于240°。并且在红色和绿色之间60°位置增加了黄色,绿色和蓝色之间180°位置增加了青色,蓝色和红色之间300°位置增加洋红色。然后通过这6种颜色进行过度,就得出的一个360°的色相环。

饱和度S就是指颜色的强度或者说纯度,通过百分比0~100%来表示。数值越大,颜色看起来就越鲜艳,数值越小,看起来就越暗淡。所呈现出来的就是从灰色到色相颜色的过度。

亮度L就是表示颜色的明暗程度,也是用百分比0~100%来表示。主要反应的就是颜色中的黑、白两种颜色,小于50%时,混入的黑色就越多,大于50%的时候,混入的白色就越多。当数值为0时,表现出来的颜色就是纯黑,数值为100%时,表现出来的颜色就是纯白,不管另外两个通道数值为多少的时候。

HSV模型和HSL模型一样,H也是表示色相,不同的是S和V(value)。

饱和度S虽然也是指颜色的纯度,但它反映出来的是混入白色的值,值越大,混入白色越少,颜色就越纯,值越小,混入的白色就越多。数值为0时,表现出来的就是纯白。

明度V它所表现的明暗程度是指混入黑色的程度。数值越小,混入黑色就越多,数值越大,混入的黑色就越少。数值为0时,表现的就是纯黑。

组件UI的实现

了解了这些颜色模型的原理,那我们现在就可以开始上代码了。

细心的同学应该发现了,为什么要介绍HSV模型呢?浏览器其实是不支持该颜色模型的。这其实主要是为了封装组件的需要,现在市面上的颜色选择组件,基本上都是根据HSV颜色模型实现的。至于为什么,一种是为了统一吧,一些共识的标准不要去改变它;第二个应该也是方便UI的实现,需要将这些三维的颜色模型展开成二维,而HSV展开后更符合人的直观视觉。

怎么展开?

  • 一般是将色相H展开成一维的线形(通过0°~360°的圆环或者是线段表示);
  • 饱和度S明度V展开成二维的矩形,x轴表示饱和度S,y轴表示明度V;
  • 如果需要透明度A,另外再增加一条一维的线段。上面没有着重介绍透明度,因为这些颜色模型透明度都是一样的,算是一个通用的颜色通道吧。

下面看UI的实现(注意:组件基于uniapp + vue3,只展示部分代码):

色相的实现

<view class="hue"></view>
.hue {
  /* 色相:从左到右依次为 红、黄、绿、青、蓝、洋红,等分渐变 */
  background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
}

饱和度和明度矩形的实现

<!-- 对比度和明度的矩形 -->
<view class="sv" :style="{ background: hColor }">
  <view class="s-mask">
    <view class="v-mask"></view>
  </view>
</view>

其中hColor表示色相环中选择的颜色。

.s-mask {
  /* 饱和度x轴实现:从左到右,纯白 >>> 透明的渐变 */
  background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
}
.v-mask {
  height: 150px;
  /* 明度y轴的实现:从下到上,纯黑 >>> 透明的渐变 */
  background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
}

放一张最终的效果图:

如果想看具体效果,可以去我的微信小程序体验,搜索【涂图了】就可以找到。

组件逻辑的实现

用到的第三方库:

  • color-convert

主要是为了不同颜色模型之间数值的转换,如果你有时间,也可以去了解他们之间的转换算法,自己实现。

定义组件的props

const props = defineProps({
  value: {
    type: String,
    default: '#000',
  },
});

为了兼容所有的颜色模型,在拿到props参数之后,都需要转成我们组件内部实现的HSV模型。

const setDefaultValue = (color: string) => {
  let defaultHSV: HSV = [0, 0, 0];
  if (/^#/.test(color)) {
    defaultHSV = hex.hsv(color);
  } else if (/^rgb/.test(color)) {
    defaultHSV = rgb.hsv(color.match(/\d+/g) as unknown as RGB);
  } else if (/^hsl/.test(color)) {
    defaultHSV = hsl.hsv(color.match(/\d+/g) as unknown as HSL);
  } else if (/^hsv/.test(color)) {
    defaultHSV = color.match(/\d+/g) as unknown as HSL;
  }

  console.log(color, '===> hsv', defaultHSV);
}

在拿到HSV的各个数值之后,就可以对我们的组件进行初始化了。由于代码量较多,就不贴代码了,大概就是对拖拽控件的偏移量进行初始化。

同时,在组件内部修改了颜色之后,也需要通过事件传出去,不要忘了将HSV模型转成浏览器能识别的模型。

const emit = defineEmits<{
  (event: 'change', value: string): void;
}>();

触发时机可以是实时的touchmove,也可以是在touchend时,或者在组件内部加一个确认按钮来触发。根据个人需求以及性能来权衡,我是在touchend时触发change事件的。

关于颜色选择组件ColorPicker的封装就到这里了。最后,再分享一个干货小福利。

颜色转换小技巧

如何快速将其他颜色模型转换成RGB呢?

直接看代码:

const $div = document.createElement('div');
$div.style.color = 'red';
document.body.appendChild($div);

const colorRGB = window.getComputedStyle($div).color;
console.log(colorRGB); // rgb(255, 0, 0)

document.body.removeChild($div);

将上面代码直接复制到你的浏览器控制台试一下,其主要是通过浏览器原生的APIgetComputedStyle来实现的,它可以将浏览器支持的任意颜色,自动转换成RGB/RGBA来返回。

最后

感谢你的阅读,如果觉得不错,记得点赞和分享!

关于文章中组件的实现效果可参考我的小程序【涂图了】进行体验,同时也可以关注我的公众号【末日码农】,获取更多技术相关知识~


末日码农
2 声望1 粉丝

一个前端码农,会写一点代码!