颜色选择器组件是一种用户界面元素,用于在用户界面中选择一个颜色。它广泛应用于网页设计、图形处理和创意软件中,允许用户以直观的方式从调色板中选择颜色。在不同的前端框架和库中,颜色选择器组件的实现可能有所不同,但它们的基本功能和目的是相同的。UniApp低代码-颜色选择器diy-color-picker-代码生成器兼容微信小程序、APP、H5。
颜色选择器组件的功能
颜色选择:颜色选择器组件通常提供一个可视化的颜色盘或颜色网格,用户可以从中选择颜色。这些颜色通常按照色彩空间(如RGB、HSV或HEX)进行组织。
格式转换:组件支持不同的颜色格式,如HEX、RGB、HSL等,并能在这些格式之间进行转换。例如,当用户选择一个颜色时,颜色选择器可以显示其在不同格式下的值。
透明度选择:一些颜色选择器还支持透明度的选择,允许用户调整颜色的alpha通道,这对于需要处理图像蒙层或颜色叠加的场景非常有用。
颜色预览:在选择颜色时,颜色选择器通常提供即时预览,让用户看到所选颜色的效果,确保选择的准确性和满意度。
预定义颜色:许多颜色选择器组件允许用户或开发人员设置预定义颜色,这些颜色可以根据特定的设计系统或品牌指南进行配置。
颜色选择器的技术实现
基于HTML5的颜色选择器:现代浏览器提供了原生的颜色选择器控件,可以通过HTML5的<input type="color">标签直接使用。这种颜色选择器简单易用,但样式和功能较为基础。
基于JavaScript的颜色选择器:通过JavaScript和CSS,可以实现更复杂、定制化程度更高的颜色选择器。这些颜色选择器可以集成到现有的Web应用程序中,提供丰富的交互和配置选项。
基于Vue的颜色选择器:在Vue等现代前端框架中,颜色选择器可以作为可重用的组件实现。例如,Ant Design、Vuetify、Element UI、Quasar等UI库提供了ColorPicker组件,这些组件可以轻松集成到Vue项目中。
颜色选择器的应用场景
- 网页设计:在网页设计工具中使用颜色选择器来设置背景色、字体色、边框色等,帮助设计师精确控制页面元素的视觉效果。兼容微信小程序、APP、H5
- 用户界面设计:在设计用户界面时,颜色选择器用于设置控件的颜色、图标颜色等,确保界面美观且符合设计规范。
颜色选择器的优势与挑战
- 优势:颜色选择器提供了一种直观、便捷的颜色选择方式,大大减少了手动输入颜色代码的麻烦。兼容微信小程序、APP、H5
- 挑战:尽管颜色选择器方便了颜色的选择和管理,但在不同设备和屏幕之间保持颜色的一致性仍然是一个挑战。此外,满足专业用户对颜色精度和灵活性的高级需求也需不断优化和更新。
- diy-color-picker组件库实现
diy-color-picker兼容微信小程序、APP、H5
<template> <view v-show="show" class="t-wrapper" @touchmove.stop.prevent="moveHandle"> <view class="t-mask active" :class="{active:active}" @click.stop="close"></view> <view class="t-box" :class="{active:active}"> <view class="t-header"> <view class="t-header-button" @click.stop="close">取消</view> <view class="t-header-button">{{hex}}</view> <view class="t-header-button" @click.stop="confirm">确认</view> </view> <view class="t-color__box" :style="{ background: 'rgb(' + bgcolor.r + ',' + bgcolor.g + ',' + bgcolor.b + ')'}"> <view class="t-background boxs" @touchstart="touchstart($event, 0)" @touchmove="touchmove($event, 0)" @touchend="touchend($event, 0)"> <view class="t-color-mask"></view> <view class="t-pointer" :style="{ top: site[0].top - 8 + 'px', left: site[0].left - 8 + 'px' }"></view> </view> </view> <view class="t-control__box"> <view class="t-control__color"> <view class="t-control__color-content" :style="{ background: 'rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',' + rgba.a + ')' }"></view> </view> <view class="t-control-box__item"> <view class="t-controller boxs" @touchstart="touchstart($event, 1)" @touchmove="touchmove($event, 1)" @touchend="touchend($event, 1)"> <view class="t-hue"> <view class="t-circle" :style="{ left: site[1].left - 12 + 'px' }"></view> </view> </view> <view class="t-controller boxs" @touchstart="touchstart($event, 2)" @touchmove="touchmove($event, 2)" @touchend="touchend($event, 2)"> <view class="t-transparency"> <view class="t-circle" :style="{ left: site[2].left - 12 + 'px' }"></view> </view> </view> </view> </view> <view class="t-alternative"> <view class="t-alternative__item" v-for="(item,index) in colorList" :key="index"> <view class="t-alternative__item-content" :style="{ background: 'rgba(' + item.r + ',' + item.g + ',' + item.b + ',' + item.a + ')' }" @click="selectColor(item)"> </view> </view> </view> </view> </view> </template> <script> import Emitter from "../../libs/util/emitter.js"; export default { mixins: [Emitter], emits: ["update:modelValue", "change","confirm"], props: { value: { type: Boolean, default: false }, modelValue: { type: Boolean, default: false }, color: { type: Object, default: null }, hexcolor: { type: String, default: '' }, spareColor: { type: Array, default () { return [] } } }, computed: { valueCom() { // #ifndef VUE3 return this.value; // #endif // #ifdef VUE3 return this.modelValue; // #endif } }, mounted() { // 组件渲染完成时,检查value是否为true,如果是,弹出popup if(this.valueCom){ this.open() } }, data() { return { show: false, active: false, // rgba 颜色 rgba: { r: 0, g: 0, b: 0, a: 1 }, // hsb 颜色 hsb: { h: 0, s: 0, b: 0 }, site: [{ top: 0, left: 0 }, { left: 0 }, { left: 0 }], index: 0, bgcolor: { r: 255, g: 0, b: 0, a: 1 }, hex: '#000000', mode: true, colorList: [{ r: 244, g: 67, b: 54, a: 1 }, { r: 233, g: 30, b: 99, a: 1 }, { r: 156, g: 39, b: 176, a: 1 }, { r: 103, g: 58, b: 183, a: 1 }, { r: 63, g: 81, b: 181, a: 1 }, { r: 33, g: 150, b: 243, a: 1 }, { r: 3, g: 169, b: 244, a: 1 }, { r: 0, g: 188, b: 212, a: 1 }, { r: 0, g: 150, b: 136, a: 1 }, { r: 76, g: 175, b: 80, a: 1 }, { r: 139, g: 195, b: 74, a: 1 }, { r: 205, g: 220, b: 57, a: 1 }, { r: 255, g: 235, b: 59, a: 1 }, { r: 255, g: 193, b: 7, a: 1 }, { r: 255, g: 152, b: 0, a: 1 }, { r: 255, g: 87, b: 34, a: 1 }, { r: 121, g: 85, b: 72, a: 1 }, { r: 158, g: 158, b: 158, a: 1 }, { r: 0, g: 0, b: 0, a: 0.5 }, { r: 0, g: 0, b: 0, a: 0 }, ] }; }, created() { // if(this.color){} // console.log(this.color); // this.rgba = this.color; if (this.spareColor.length !== 0) { this.colorList = this.spareColor; } }, methods: { /** * 初始化 */ init() { // hsb 颜色 if(this.color){ this.rgba=this.color; this.hsb = this.rgbToHex(this.rgba); }else if(this.hexcolor){ let hsb=this.hexcolor;//.replace('#',''); let rgba=this.hexToRgba(hsb); this.rgba=rgba; this.hsb = hsb; } // this.setColor(); this.setValue(this.rgba); }, moveHandle() {}, open() { this.show = true; this.$nextTick(() => { this.init(); setTimeout(() => { this.active = true; setTimeout(() => { this.getSelectorQuery(); }, 400) }, 5) }) }, close() { this.active = false; this.$nextTick(() => { setTimeout(() => { this.show = false; this.$emit("update:modelValue", false); }, 300) }) }, confirm() { this.close(); this.$emit('confirm', { rgba: this.rgba, hex: this.hex }) }, // 选择模式 select() { this.mode = !this.mode }, // 常用颜色选择 selectColor(item) { this.setColorBySelect(item) }, //触摸开始事件 touchstart(e, index) { const { pageX, pageY } = e.touches[0]; this.pageX = pageX; this.pageY = pageY; this.setPosition(pageX, pageY, index); }, //手指滑动过程 touchmove(e, index) { const { pageX, pageY } = e.touches[0]; this.moveX = pageX; this.moveY = pageY; this.setPosition(pageX, pageY, index); }, //触摸结束事件 touchend(e, index) {}, /** * 设置位置 */ setPosition(x, y, index) { this.index = index; const { top, left, width, height } = this.position[index]; // 设置最大最小值 this.site[index].left = Math.max(0, Math.min(parseInt(x - left), width)); if (index === 0) { this.site[index].top = Math.max(0, Math.min(parseInt(y - top), height)); // 设置颜色 this.hsb.s = parseInt((100 * this.site[index].left) / width); this.hsb.b = parseInt(100 - (100 * this.site[index].top) / height); this.setColor(); this.setValue(this.rgba); } else { this.setControl(index, this.site[index].left); } }, /** * 设置 rgb 颜色 */ setColor() { const rgb = this.HSBToRGB(this.hsb); this.rgba.r = rgb.r; this.rgba.g = rgb.g; this.rgba.b = rgb.b; }, /** * 设置二进制颜色 * @param {Object} rgb */ setValue(rgb) { this.hex = '#' + this.rgbToHex(rgb); }, //设置透明度 setControl(index, x) { const { top, left, width, height } = this.position[index]; if (index === 1) { this.hsb.h = parseInt((360 * x) / width); this.bgcolor = this.HSBToRGB({ h: this.hsb.h, s: 100, b: 100 }); this.setColor() } else { this.rgba.a = (x / width).toFixed(1); } this.setValue(this.rgba); }, /** * @param {Object} hex hex转RGB */ hexToRgba(hex) { // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") let shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; hex = hex.replace(shorthandRegex, function(m, r, g, b) { return r + r + g + g + b + b; }); let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16), a: result[4] ? parseInt(result[4], 16) / 255 : 1, } : null; }, /** * rgb 转 二进制 hex * @param {Object} rgb */ rgbToHex(rgb) { let hex = [rgb.r.toString(16), rgb.g.toString(16), rgb.b.toString(16)]; hex.map(function(str, i) { if (str.length == 1) { hex[i] = '0' + str; } }); return hex.join(''); }, setColorBySelect(getrgb) { const { r, g, b, a } = getrgb; let rgb = {} rgb = { r: r ? parseInt(r) : 0, g: g ? parseInt(g) : 0, b: b ? parseInt(b) : 0, a: a ? a : 0, }; this.rgba = rgb; this.hsb = this.rgbToHsb(rgb); this.changeViewByHsb(); }, changeViewByHsb() { // console.log("changeViewByHsb"); const [a, b, c] = this.position; this.site[0].left = parseInt(this.hsb.s * a.width / 100); this.site[0].top = parseInt((100 - this.hsb.b) * a.height / 100); this.setColor(this.hsb.h); this.setValue(this.rgba); this.bgcolor = this.HSBToRGB({ h: this.hsb.h, s: 100, b: 100 }); this.site[1].left = this.hsb.h / 360 * b.width; this.site[2].left = this.rgba.a * c.width; }, /** * hsb 转 rgb * @param {Object} 颜色模式 H(hues)表示色相,S(saturation)表示饱和度,B(brightness)表示亮度 */ HSBToRGB(hsb) { let rgb = {}; let h = Math.round(hsb.h); let s = Math.round((hsb.s * 255) / 100); let v = Math.round((hsb.b * 255) / 100); if (s == 0) { rgb.r = rgb.g = rgb.b = v; } else { let t1 = v; let t2 = ((255 - s) * v) / 255; let t3 = ((t1 - t2) * (h % 60)) / 60; if (h == 360) h = 0; if (h < 60) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; } else if (h < 120) { rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; } else if (h < 180) { rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; } else if (h < 240) { rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; } else if (h < 300) { rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; } else if (h < 360) { rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; } else { rgb.r = 0; rgb.g = 0; rgb.b = 0; } } return { r: Math.round(rgb.r), g: Math.round(rgb.g), b: Math.round(rgb.b) }; }, rgbToHsb(rgb) { let hsb = { h: 0, s: 0, b: 0 }; let min = Math.min(rgb.r, rgb.g, rgb.b); let max = Math.max(rgb.r, rgb.g, rgb.b); let delta = max - min; hsb.b = max; hsb.s = max != 0 ? 255 * delta / max : 0; if (hsb.s != 0) { if (rgb.r == max) hsb.h = (rgb.g - rgb.b) / delta; else if (rgb.g == max) hsb.h = 2 + (rgb.b - rgb.r) / delta; else hsb.h = 4 + (rgb.r - rgb.g) / delta; } else hsb.h = -1; hsb.h *= 60; if (hsb.h < 0) hsb.h = 0; hsb.s *= 100 / 255; hsb.b *= 100 / 255; return hsb; }, getSelectorQuery() { const views = uni.createSelectorQuery().in(this); views .selectAll('.boxs') .boundingClientRect(data => { if (!data || data.length === 0) { setTimeout(() => this.getSelectorQuery(), 20) return } this.position = data; // this.site[0].top = data[0].height; // this.site[0].left = 0; // this.site[1].left = data[1].width; // this.site[2].left = data[2].width; this.setColorBySelect(this.rgba); }) .exec(); } }, watch: { valueCom(val){ if(val){ this.open() }else{ this.close() } }, spareColor(newVal) { this.colorList = newVal; } } }; </script> <style> .t-wrapper { position: fixed; top: 0; bottom: 0; left: 0; width: 100%; box-sizing: border-box; z-index: 999999; } .t-box { width: 100%; position: absolute; bottom: 0; padding: 30upx 0; padding-top: 0; background: #fff; transition: all 0.3s; transform: translateY(100%); } .t-box.active { transform: translateY(0%); } .t-header { display: flex; justify-content: space-between; width: 100%; height: 100upx; border-bottom: 1px #eee solid; box-shadow: 1px 0 2px rgba(0, 0, 0, 0.1); background: #fff; } .t-header-button { display: flex; align-items: center; width: 150upx; height: 100upx; font-size: 30upx; color: #666; padding-left: 20upx; } .t-header-button:last-child { justify-content: flex-end; padding-right: 20upx; } .t-mask { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.6); z-index: -1; transition: all 0.3s; opacity: 0; } .t-mask.active { opacity: 1; } .t-color__box { position: relative; height: 400upx; background: rgb(255, 0, 0); overflow: hidden; box-sizing: border-box; margin: 0 20upx; margin-top: 20upx; box-sizing: border-box; } .t-background { position: absolute; z-index: 16777271; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0)); } .t-color-mask { position: absolute; z-index: 16777271; top: 0; left: 0; right: 0; bottom: 0; width: 100%; height: 400upx; background: linear-gradient(to top, #000, rgba(0, 0, 0, 0)); } .t-pointer { position: absolute; bottom: -8px; left: -8px; z-index: 2; width: 15px; height: 15px; border: 1px #eee solid; border-radius: 50%; } .t-show-color { width: 100upx; height: 50upx; } .t-control__box { margin-top: 50upx; width: 100%; display: flex; padding-left: 20upx; box-sizing: border-box; } .t-control__color { flex-shrink: 0; width: 100upx; height: 100upx; border-radius: 50%; background-color: #fff; background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee), linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee); background-size: 36upx 36upx; background-position: 0 0, 18upx 18upx; border: 1px #eee solid; overflow: hidden; } .t-control__color-content { width: 100%; height: 100%; } .t-control-box__item { display: flex; flex-direction: column; justify-content: space-between; width: 100%; padding: 0 30upx; } .t-controller { position: relative; z-index: 16777271; width: 100%; height: 16px; background-color: #fff; background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee), linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee); background-size: 32upx 32upx; background-position: 0 0, 16upx 16upx; } .t-hue { width: 100%; height: 100%; background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); } .t-transparency { width: 100%; height: 100%; background: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0)); } .t-circle { position: absolute; /* right: -10px; */ top: -2px; width: 20px; height: 20px; box-sizing: border-box; border-radius: 50%; background: #fff; box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1); } .t-result__box { margin-top: 20upx; padding: 10upx; width: 100%; display: flex; box-sizing: border-box; } .t-result__item { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 10upx; width: 100%; box-sizing: border-box; } .t-result__box-input { padding: 10upx 0; width: 100%; font-size: 28upx; box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1); color: #999; text-align: center; background: #fff; } .t-result__box-text { margin-top: 10upx; font-size: 28upx; line-height: 2; } .t-select { flex-shrink: 0; width: 150upx; padding: 0 30upx; } .t-select .t-result__box-input { border-radius: 10upx; border: none; color: #999; box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.1); background: #fff; } .t-select .t-result__box-input:active { box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.1); } .t-alternative { display: flex; flex-wrap: wrap; /* justify-content: space-between; */ width: 100%; padding-right: 10upx; box-sizing: border-box; } .t-alternative__item { margin-left: 12upx; margin-top: 10upx; width: 50upx; height: 50upx; border-radius: 10upx; background-color: #fff; background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee), linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee); background-size: 36upx 36upx; background-position: 0 0, 18upx 18upx; border: 1px #eee solid; overflow: hidden; } .t-alternative__item-content { width: 50upx; height: 50upx; background: rgba(255, 0, 0, 0.5); } .t-alternative__item:active { transition: all 0.3s; transform: scale(1.1); } </style>
- 组件使用
<template> <view class="container container23285"> <u-form-item class="diygw-col-24" label="选择颜色" prop="colorinput"> <u-input @tap="showColorinput = true" placeholder="请输入颜色值" v-model="colorinput"></u-input> <text class="diygw-text-lg diy-icon-colorlens" @tap="showColorinput = true" :style="{ color: colorinput }"></text> <diy-color-picker v-model="showColorinput" :hexcolor="colorinput" @confirm="changeColorinput"></diy-color-picker> </u-form-item> <view class="clearfix"></view> </view> </template> <script> export default { data() { return { //用户全局信息 userInfo: {}, //页面传参 globalOption: {}, //自定义全局变量 globalData: {}, showColorinput: false, colorinput: '' }; }, onShow() { this.setCurrentPage(this); }, onLoad(option) { this.setCurrentPage(this); if (option) { this.setData({ globalOption: this.getOption(option) }); } this.init(); }, methods: { async init() {}, changeColorinput(evt) { let result = evt.hex; this.colorinput = result; } } }; </script> <style lang="scss" scoped> .container23285 { } </style>
总的来说,颜色选择器组件在现代数字设计和开发中扮演着重要角色,从基本的网页颜色设置到专业的图形处理软件都离不开它。通过不断的技术创新和应用优化,颜色选择器不仅提升了用户体验,也提高了设计和开发的效率。