Vue自定义组件:颜色选择器及其实现原理
老样纸,先上效果图:
demo操作地址:plain-ui demo
打开这个页面之后,在Form中打开ColorPicker颜色选择页面
先对这个效果图做一个简单介绍:
- 这个组件左边的实例是启用了设置透明度alpha值的,而右边的禁用了,所以是看起来两种不同的效果;
- 这两个实例通过v-model绑定了同一个值,所以在点击确定按钮的时候,两个都产生了相应的变化,因为右边的没有启用透明度,所以转化得到的值会自动转化为不带透明度的16进制,这个转化的结果是可以设置的,支持rgb以及16进制(hex),默认是16进制;
- 从上至下,第一个正方形是用来选择饱和度(saturation)以及亮度(value),第一个七彩的滑条是用来选择色相(hue),第三个是记录历史选择的颜色,可以点击历史选择的颜色快速选中颜色;
- 第行是颜色输入框,输入颜色,回车之后会更新显示输入的颜色,点击确定按钮这个组件实例会派发input事件,值就是选中的颜色;
接下来介绍这个组件的实现原理
在实现这个组件的时候,网上各种百度谷歌博客网页,大多数都是介绍啥啥啥组件怎么用,哪个现成的比较好用,就没看到那篇博客详细说明了这个东西的实现原理,每个东西是怎么来的,这里我研究了一番之后,决定将解析过程写下来,避免过一段时间就忘了。
-
首先介绍一个核心概念:HSV颜色模型
1) 先上图:
2)在HSV颜色模型中,HSV三个字母分别代表色调(hue)、饱和度(saturation)、亮度(value),首先色调的取值范围是0 ~ 360,表示0度到360度,对应图中圆锥上方彩色的度数,0度以及360度都表示红色,120度表示绿色,240度表示蓝色;饱和度的取值范围为0 ~ 100,可以理解为点到彩色圆中心的距离,观察一下上面的图,在圆锥彩色圆中,圆心是白色的,这时饱和度为0,当饱和度为100时,颜色就是圆最外层的颜色,也就是最“饱和”的颜色,所以这个饱和度可以理解为在彩色圆中的点到彩色圆中心的距离,从立体角度来看,就是圆锥体内的点,到圆锥内部中心轴的距离;亮度为圆锥体内的点所在的平面到圆锥顶点的距离,为0,表示亮度为0,这时就是黑色的,如果为最大值100,表示点在彩色圆上;当hsv为0,100,100表示为红色;
3)看这个图,在圆点上下移动的时候观察value,在圆点左右移动的时候观察saturation:
可以观察到,当saturation越小时,越趋近于白色(饱和度低),当value越小时(亮度低),越趋近于黑色,当saturation以及value都为100时,就是当前色调的颜色; -
相关组件罗列(按照实现的顺序):
1)pl-color-hue-slider(色调滑条选择组件,图中七彩横条滑动的组件);
2)pl-color-sv-picker(饱和度以及亮度选择组件,图中正方形元素);
3)pl-color-alpha-slider(透明度选择组件,本文最开始的图中第一张图色调滑条选择组件下面的有透明格子的组件);
4)pl-color(显示颜色的小圆圈组件);
5)pl-color-history(显示历史选择颜色的组件,点击可以得到选择的历史颜色);
6)pl-color-picker-panel(整个颜色选择面板组件); -
pl-color-hue-slider没啥技术含量,就是背景色是: background: linear-gradient(90deg, red 0, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, red);然后有个div可以拖拽左右移动,这个左右移动是通过left属性控制的,通过left值与整个滑条可滑动宽度(滑条宽度减去滑块宽度)之间的比例([0,100])得出[0,360]的度数;
-
pl-color-sv-picker要比pl-color-hue-slider要稍微复杂一点点,就是横轴left值是饱和度,纵轴top值是100-亮度,然后这个白色、黑色以及对应色调颜色的渐变颜色面板的原理是有三个div,第一个div背景色是色调色,第二个div是白色渐变色background: linear-gradient(to right, white, #ffffff00),从左至右,第三个div是黑色渐变色background: linear-gradient(to top, black, #ffffff00),从下至上,最后三个div覆盖在一起就可以得到这个黑白彩渐变的面板;
-
pl-color-alpha-slider透明度组件原理与pl-color-hue-slider原理基本一致;
-
剩下的pl-color-history以及pl-color-picker-panel的实现过程与颜色选择器的核心原理没啥关系,这里就不提了;