1.分析

该颜色选择器是通过HSV颜色模型实现的,默认值支持16进制和rgb格式。
  HSV颜色模型是基于色相H(hue)、饱和度S(saturation)、明度V(Value)。
    HSV中的H色相,反映HSV模型中的主色,色相是以六大主色为基础,分别按60度的间隔排列在圆环上。这六大主色分别是:0°红、60°黄、120°绿、180°青、240°蓝、300°洋红、360°红。
    HSV中的S饱和度,反映色相颜色中混入白色的值,呈现白色到色相颜色的变化;
    HSV中的V明度,体现的是从黑色到色相(H)颜色的过渡。

  如图可以认为:面板的颜色主色为红色,越往左红色中混入的白色越多,直至全部为白色。越往下红色中混入的黑色越多,直至全为黑色。
  色相柱实现
    背景: background: linear-gradient(180deg,#f00,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,#f00);
    值([0,360))为:滑块的偏移量/ 色相柱的高度 )* 360 就是色相值,色相值通过转换公式可以得到对应的rgb颜色并用于颜色面板的背景色。
  颜色面板实现
    背景: background: linear-gradient(to top, #000, transparent),linear-gradient(to right, #fff, transparent);
    饱和度([0,100])值:offsetLeft / 颜色面板宽 * 100
    亮度([0,100])值:offsetTop / 颜色面板高 * 100

2.实现

  效果如下

  代码如下

<template><div class="color-picker">//选中的颜色<div class="color-button"><div class="back-ground"><div class="contain" :style="{ backgroundColor: realShowColor }" @click="isShowDropDown"><img style="height: 100%; width: 100%" src="./down.svg" alt="" /></div></div></div>//颜色选择器<div :class="{ 'show-dropdown': isShow }" class="color-dropdown">//颜色面板<divref="colorPannel"class="color-pannel-box":style="{ backgroundColor: colorPannel.backgroundColor }"><div:style="{ top: colorPannel.top + 'px', left: colorPannel.left + 'px' }"class="color-select-circle"@mousedown="pannelMosueHandler($event)"></div></div>//色相柱<div ref="colorBar" class="color-slider-box"><div class="color-slider"></div><divclass="color-thumb":style="{ top: colorBar.top + 'px' }"@mousedown="thumbMouseHandler($event)"></div></div>//透明条<div v-if="showAlpha" ref="alphaBar" class="color-alpha"><divclass="color-alpha-bar":style="{background: `linear-gradient(to right, ${alphaColorBar.barColor}, ${rgbToRgba(alphaColorBar.barColor,0)})`}"></div><divclass="color-alpha-thumb":style="{ left: alphaColorBar.thumbLeft + 'px' }"@mousedown="alphaBarMouseHandler($event)"></div></div><div class="color-input"><input v-model="realShowColor" class="color-input-box" type="text" /><button @click="submitColor">确定</button></div></div></div>
</template><script>
export default {name: 'ColorPicker',props: {// 是否开启透明度showAlpha: {type: Boolean,default() {return true}},//初始化颜色,使用该组件时传入的默认颜色:支持hex、rgb格式initColor: {type: String,default() {return '#f00'}},//input中展示的颜色格式: hex、rgbcolorFormat: {type: String,default() {return 'hex'}}},data() {return {colorConfig: {h: 360,s: 100,v: 100,alpha: 1,value: '',//底色basicColor: ''},alphaColorBar: {barColor: 'rgb(255, 0, 0)',thumbLeft: 0,width: 0},colorBar: {top: 0,height: 0},colorPannel: {top: 0,left: 300,backgroundColor: '#f00',height: 0,width: 0},//默认红色realShowColor: '#f00',isShow: false,isApply: false}},mounted() {this.initShowColor(this.initColor)},methods: {//初始化initShowColor(color) {//初始化hsv颜色、格式化的颜色值let hsvObj, initRgbif (color.indexOf('#') !== -1) {initRgb = this.hexToRGB(color)hsvObj = this.rgbToHSV(initRgb)} else if (color.indexOf('rgb') !== -1) {hsvObj = this.rgbToHSV(color)} else {throw new Error('初始化颜色格式错误,使用#fff或rgb格式')}if (hsvObj) {this.colorConfig.h = hsvObj.hthis.colorConfig.s = hsvObj.sthis.colorConfig.v = hsvObj.v}//获取容器高宽this.colorBar.height = this.$refs.colorBar.getBoundingClientRect().heightthis.colorPannel.height = this.$refs.colorPannel.getBoundingClientRect().heightthis.colorPannel.width = this.$refs.colorPannel.getBoundingClientRect().widthif (this.showAlpha) {this.alphaColorBar.width = this.$refs.alphaBar.getBoundingClientRect().width// 根据alpha获取滑块位置this.alphaToPosition(this.colorConfig.alpha, this.alphaColorBar.width)this.alphaColorBar.barColor = initRgb || color}//根据hsv获取位置this.colorPannel.backgroundColor = this.hueToRGB(this.colorConfig.h)this.hsvToPosition(this.colorConfig.s,this.colorConfig.v,this.colorPannel.width,this.colorPannel.height)this.hueToPosition(this.colorConfig.h, this.colorBar.height)//根据colorFormat和showAlpha格式化颜色this.colorForamtTransform()this.realShowColor = this.colorConfig.value || this.initColor},isShowDropDown() {this.isShow = !this.isShow},submitColor() {//如果颜色为rgba形式将转换为rgb。let initColorif (this.realShowColor.indexOf('rgba') !== -1) {initColor = this.realShowColor.replace(/,\d{1,3}(?=\))/, '')//获取输入的alphathis.colorConfig.alpha = parseFloat(this.realShowColor.split(',')[3].replace(')', ''))this.colorConfig.alpha = Math.max(0, this.colorConfig.alpha)this.colorConfig.alpha = Math.min(this.colorConfig.alpha, 1)} else {initColor = this.realShowColor}this.initShowColor(initColor)this.isShow = false},//色相柱的拖拽事件thumbMouseHandler(e) {if (e.type === 'mousedown') {document.body.addEventListener('mousemove', this.thumbMouseHandler)document.body.addEventListener('mouseup', this.thumbMouseHandler)} else if (e.type === 'mousemove') {const elemInfo = this.$refs.colorBar.getBoundingClientRect()this.colorBar.top = e.clientY - elemInfo.topthis.colorBar.top = Math.max(0, this.colorBar.top)this.colorBar.top = Math.min(this.colorBar.top, elemInfo.height)this.colorConfig.h = ((parseInt(this.colorBar.top) / elemInfo.height) * 360 * 100) / 100//色相[0,360)if (this.colorConfig.h === 360) {this.colorConfig.h = 0}//获取颜色面板背景色this.colorPannel.backgroundColor = this.hueToRGB(this.colorConfig.h)this.colorForamtTransform()this.alphaColorBar.barColor = this.colorConfig.basicColorthis.realShowColor = this.colorConfig.value} else if (e.type === 'mouseup') {// 当释放鼠标键时,删除鼠标移动事件和删除鼠标释放事件document.body.removeEventListener('mousemove', this.thumbMouseHandler)document.body.removeEventListener('mouseup', this.thumbMouseHandler)}},//颜色面板的拖拽事件pannelMosueHandler(e) {if (e.type === 'mousedown') {document.body.addEventListener('mousemove', this.pannelMosueHandler)document.body.addEventListener('mouseup', this.pannelMosueHandler)} else if (e.type === 'mousemove') {const elemInfo = this.$refs.colorPannel.getBoundingClientRect()//在颜色面板容器范围内移动this.colorPannel.top = e.clientY - elemInfo.topthis.colorPannel.left = e.clientX - elemInfo.left//使取色圈移动更加顺滑且不超过取色面板容器范围this.colorPannel.left = Math.max(0, this.colorPannel.left)this.colorPannel.left = Math.min(this.colorPannel.left, elemInfo.width)this.colorPannel.top = Math.max(0, this.colorPannel.top)this.colorPannel.top = Math.min(this.colorPannel.top, elemInfo.height)//计算饱和度(0 -> 100)和亮度 (0 -> 100)this.colorConfig.s = (parseInt(this.colorPannel.left) / elemInfo.width) * 100this.colorConfig.v = (1 - parseInt(this.colorPannel.top) / elemInfo.height) * 100this.colorForamtTransform()//将hsv转换为rgbthis.alphaColorBar.barColor = this.colorConfig.basicColorthis.realShowColor = this.colorConfig.value} else if (e.type === 'mouseup') {// 当释放鼠标键时,删除鼠标移动事件和删除鼠标释放事件document.body.removeEventListener('mousemove', this.pannelMosueHandler)document.body.removeEventListener('mouseup', this.pannelMosueHandler)}},//透明柱的拖拽事件alphaBarMouseHandler(e) {if (e.type === 'mousedown') {document.body.addEventListener('mousemove', this.alphaBarMouseHandler)document.body.addEventListener('mouseup', this.alphaBarMouseHandler)} else if (e.type === 'mousemove') {const elemInfo = this.$refs.alphaBar.getBoundingClientRect()this.alphaColorBar.thumbLeft = e.clientX - elemInfo.leftthis.alphaColorBar.thumbLeft = Math.max(0, this.alphaColorBar.thumbLeft)this.alphaColorBar.thumbLeft = Math.min(this.alphaColorBar.thumbLeft, elemInfo.width)//获取颜色透明度0 -> 1this.colorConfig.alpha = (1 - this.alphaColorBar.thumbLeft / elemInfo.width).toFixed(2)this.colorForamtTransform()this.realShowColor = this.colorConfig.value} else if (e.type === 'mouseup') {// 当释放鼠标键时,删除鼠标移动事件和删除鼠标释放事件document.body.removeEventListener('mousemove', this.alphaBarMouseHandler)document.body.removeEventListener('mouseup', this.alphaBarMouseHandler)}},//颜色格式colorForamtTransform() {if (this.showAlpha) {//如果开启透明度,那么颜色一定为rgba格式this.colorConfig.basicColor = this.hsvToRGB(this.colorConfig.h,this.colorConfig.s,this.colorConfig.v)this.colorConfig.value = this.rgbToRgba(this.colorConfig.basicColor, this.colorConfig.alpha)} else {if (this.colorFormat === 'hex') {this.colorConfig.basicColor = this.hsvToRGB(this.colorConfig.h,this.colorConfig.s,this.colorConfig.v)this.colorConfig.value = this.rgbToHex(this.colorConfig.basicColor)}if (this.colorFormat === 'rgb') {this.colorConfig.basicColor = this.hsvToRGB(this.colorConfig.h,this.colorConfig.s,this.colorConfig.v)this.colorConfig.value = this.colorConfig.basicColor}}},//从hue to rgbhueToRGB(h) {if (h === 360) {h = 0}let doHandle = num => {if (num > 255) {return 255} else if (num < 0) {return 0} else {return Math.round(num)}}let hueRGB = (h / 60) * 255let r = doHandle(Math.abs(hueRGB - 765) - 255)let g = doHandle(510 - Math.abs(hueRGB - 510))let b = doHandle(510 - Math.abs(hueRGB - 1020))return 'rgb(' + r + ',' + g + ',' + b + ')'},//从HSV(色相、饱和度、亮度) to rgbhsvToRGB(h, s, v) {s = s / 100v = v / 100let r = 0,g = 0,b = 0let i, f, p, q, ti = Math.floor(h / 60)f = h / 60 - ip = v * (1 - s)q = v * (1 - f * s)t = v * (1 - (1 - f) * s)switch (i) {case 0:r = vg = tb = pbreakcase 1:r = qg = vb = pbreakcase 2:r = pg = vb = tbreakcase 3:r = pg = qb = vbreakcase 4:r = tg = pb = vbreakcase 5:r = vg = pb = qbreak}return `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`},//rgb to  hsvrgbToHSV(rgbStr) {let { r, g, b } = this.getRGB(rgbStr)r = parseFloat(parseFloat(r / 255).toFixed(4))g = parseFloat(parseFloat(g / 255).toFixed(4))b = parseFloat(parseFloat(b / 255).toFixed(4))const max = Math.max(r, g, b)const min = Math.min(r, g, b)let h, slet v = maxconst d = max - mins = max === 0 ? 0 : d / maxif (max === min) {h = 0 // achromatic} else {switch (max) {case r:h = (g - b) / d + (g < b ? 6 : 0)breakcase g:h = (b - r) / d + 2breakcase b:h = (r - g) / d + 4break}h /= 6}return { h: h * 360, s: s * 100, v: v * 100 }},//根据hsv获取取色圈位置hsvToPosition(s, v, width, height) {this.colorPannel.top = height - (v * height) / 100this.colorPannel.left = (s * width) / 100},hueToPosition(h, height) {this.colorBar.top = (h * height) / 360},alphaToPosition(alpha, width) {this.alphaColorBar.thumbLeft = (1 - alpha) * width},//拆解rgb为r,g,bgetRGB(rgbStr) {const matchArr = rgbStr.match(/\(.+?\)/g)[0].match(/\w+/g)let r = parseInt(matchArr[0])let g = parseInt(matchArr[1])let b = parseInt(matchArr[2])return { r, g, b }},// rgb 转 16进制rgbToHex(rgbStr) {//拆解rgb为[255,255,255]形式。let { r, g, b } = this.getRGB(rgbStr)return `#${this.zeroFill(r.toString(16))}${this.zeroFill(g.toString(16))}${this.zeroFill(b.toString(16))}`},rgbToRgba(rgbStr, alpha) {return rgbStr.replace(')', `,${alpha})`)},hexToRGB(hexStr) {if (hexStr.length === 4) {const hexArr = hexStr.match(/\w{1}/g)return `rgb(${parseInt(hexArr[0] + hexArr[0], 16)},${parseInt(hexArr[1] + hexArr[1],16)},${parseInt(hexArr[2] + hexArr[2], 16)})`}if (hexStr.length === 7) {const hexArr = hexStr.match(/\w{2}/g)return `rgb(${parseInt(hexArr[0], 16)},${parseInt(hexArr[1], 16)},${parseInt(hexArr[2],16)})`}},//补零zeroFill(val) {return val.length > 1 ? val : '0' + val}}
}
</script><style lang="less" scoped>
.color-picker {width: 500px;margin: auto;height: 500px;margin-top: 200px;position: relative;.color-button {height: 36px;width: 36px;border: 1px solid rgba(0, 0, 0, 0.15);border-radius: 4px;.back-ground {height: 26px;width: 26px;margin: 4px;background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);.contain {border: 1px solid rgba(0, 0, 0, 0.5);border-radius: 2px;}}}.color-dropdown {margin: auto;width: 340px;height: 0;position: absolute;top: 36px;left: -154px;background-color: rgba(0, 0, 0, 0.2);display: flex;flex-wrap: wrap;transition: height 0.5s Ease-in;align-content: space-evenly;overflow: hidden;.color-pannel-box {position: relative;width: 300px;height: 192px;margin-left: 10px;background: linear-gradient(to top, #000, transparent),linear-gradient(to right, #fff, transparent);.color-select-circle {position: absolute;transform: translate(-4px, -4px);border: 1px solid #fff;width: 8px;height: 8px;border-radius: 50%;}}.color-slider-box {cursor: pointer;width: 10px;position: relative;.color-slider {background: linear-gradient(180deg,#f00,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,#f00);width: 10px;height: 192px;margin-left: 10px;}.color-thumb {width: 18px;height: 7px;position: absolute;left: -3px;transform: translate(0, -3px);border-radius: 2px;background-color: rgb(10, 10, 10);border: 3px solid #fff;margin-left: 9px;}}.color-alpha {position: relative;height: 12px;box-shadow: 2px 2px 2px 2px 2px rgba(0, 0, 0, 0.1);margin-left: 10px;width: 300px;background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);.color-alpha-bar {height: 100%;width: 100%;}.color-alpha-thumb {width: 7px;height: 18px;position: absolute;top: -3px;transform: translate(-3px, 0);border-radius: 2px;background-color: rgb(10, 10, 10);border: 3px solid #fff;}}.color-input {width: 100%;padding: 0 10px;margin: auto;display: flex;justify-content: space-between;button {color: #000;}.color-input-box {color: #000;border: 1px solid rgba(0, 0, 0, 0.1);}}}.show-dropdown {height: 260px;transition: height 0.3s Ease-in;}
}
</style>

3.缺点

  不支持预览色。
  不支持outSideClick,即点击颜色选择器容器外部动作未做处理。

vue 实现带有透明度的颜色选择器(hsv颜色模型)相关推荐

  1. VS编程,WPF中,通过telerik控件创建颜色选择器,颜色拾取器,调色板的一种方法

     这里展示使用telerik控件时,调用颜色调色板,颜色选择器,颜色拾取器的一种方法 1.增加引用 2.前台定义引用 xmlns:telerik="http://schemas.teleri ...

  2. html简易颜色选择器,HTML颜色选择器实现代码

    HTML颜色选择器实现代码 HTML颜色选择器 范围:#000 - #FFF //  '); } document.writeln(' '); var begin = 0; for (var i = ...

  3. 使用Opencv获取每个像素点的RGB颜色分量/HSV颜色分量

    一.  所需结构体 CvScalar 结构体介绍 typedef struct CvScalar { double val[4]; //BGRA}CvScalar; 二. 所需函数 cvGet2D 函 ...

  4. python如何设置rgb颜色_【Python图像处理】RGB颜色转HSV颜色的快速实现

    传送门 思路 使用NumPy.NumPy对数组和矩阵的运算有大幅度的提速.因此,使用NumPy设计算法时,应该充分利用这一特性,尽可能用NumPy中的矩阵运算来代替遍历等耗时的操作. RGB转HSV ...

  5. 颜色选择器html组件,ColorPicker 颜色选择器

    ColorPicker 颜色选择器 ColorPicker 颜色选择器 用于颜色选择,支持多种格式. 基础用法 有默认值 无默认值 使用 v-model 与 Vue 实例中的一个变量进行双向绑定,绑定 ...

  6. 颜色选择器vue3-colorpicker

    其他选择器:一款支持vue3 的颜色选择器 | ColorPickerV3基于vue3的颜色选择器支持颜色透明度与rgba.hexhttps://colorpickerv3.wcrane.cn/gui ...

  7. 颜色代码对照表、网页颜色选择器

    颜色代码对照表.网页颜色选择器 2006-12-04 20:47:28|  分类: 博客教程 |  标签:博客  教程  |举报|字号 订阅 颜色代码大全 网页颜色选择器 ffff00 ffff33 ...

  8. 自绘按钮实现颜色选择器

    一.前言 很多时候,我们需要让用户在软件上选择颜色,那么一个"颜色选择器"就肯定需要了,本例程就是使用普通的按钮(Button)控件来实现颜色选择器. 首先来看一下最后的效果图: ...

  9. C# Color颜色RGB对照表、颜色选择器

    C# Color颜色RGB对照表.颜色选择器 C#颜色对照表 可以查询对应颜色.和颜色选择器 http://www.lzltool.com/Doc/CsharpColor

最新文章

  1. 滴滴魅族手机人脸识别没有反应_魅族Note9发布,亮点不足,价格却很有诚意
  2. mysql总结 博客园_mysql总结
  3. Linux备份MySQL xshell_linux shell脚本备份mysql数据库
  4. C# 复制 粘贴 剪切 撤销
  5. 【Boost】boost库asio详解1——io_service::run函数无任务时退出的问题
  6. (9) ab测试工具安装与使用
  7. 常见 Java 字节码 指令 助记符
  8. MGraph图(代码、分析、汇编)
  9. 国内11所“袖珍”大学!最小的甚至只有一栋楼……
  10. flash代码_Flash如何对制作文件进行优化
  11. MongoDB 教程二: 添加, 删除,查询 shell命令
  12. android开不了机怎么办手机号码,手机开不了机怎么办 原因分析及其解决方法
  13. understand 4 for linux register key code 随意、任性拥有。。。。
  14. opencv 指定分辨率_更改OpenCV视频文件的分辨率
  15. Mac上好用且免费的远程桌面SSH工具(FinalShell)
  16. GMS Apps安装
  17. MATLAB之牛顿插值法
  18. matlab正弦信号采样,matlab模拟信号的欠采样及原理理解
  19. 数据库:MySQL Workbench如何连接远程数据库
  20. 瑞萨 报错 Section “.monitor2“ overlaps section “.textf“

热门文章

  1. Excel加载项出错,显示【ExcelAPInet-Addln.xll的文件格式和扩展名不匹配】我知道的解决方案
  2. 输入字符串,计算占据英文长度
  3. x86机器PC机改造成云终端瘦终端教程
  4. css如何制作横幅,javascript-仅使用CSS的标题横幅?
  5. iOS快捷指令 | 早安,让Siri唤醒你的美好一天
  6. 移动端星级评分效果的实现
  7. 中国有机食品市场供需调研及前景预测分析报告2022-2027年
  8. aerospike数据库的使用
  9. strace -p pid
  10. 嵌入式/X86 Ubuntu/Linux系统配置VNCServer远程gnome桌面