const utils = { // 事件绑定 addHandler(target, event, handler) { if (target.addEventListener) { target.addEventListener(event, handler, false); } else if (target.attachEvent) { target.attachEvent('on' + event, handler); } else { target['on' + event] = handler; } }, // 事件解绑 removeHandler(target, event, handler) { if (target.removeEventListener) { target.removeEventListener(event, handler, false); } else if (target.detachEvent) { target.detachEvent('on' + event, handler); } else { target['on' + event] = null; } }, // 坐标锁定 clamp(val, min, max) { return val < min ? min : (val > max ? max : val); }, // 一个不考虑兼容性的domready domready(callback) { document.addEventListener('DOMContentLoaded', listener = function () { document.removeEventListener('DOMContentLoaded', listener); callback(); }); }, // 判断是否为数组 isArray(arrayLike) { return Object.prototype.toString.call(arrayLike) === '[object Array]'; }, // 数字末尾除0 trimZero(str) { return str.replace(/\.?0*$/, ''); }, color: { // https://gist.github.com/NV/522734 // hsb颜色转为hsl颜色 hsb2hsl(h, s, b) { var hsl = { h: h }; hsl.l = (2 - s) * b; hsl.s = s * b; if (hsl.l <= 1 && hsl.l > 0) { hsl.s /= hsl.l; } else { hsl.s = hsl.s / (2 - hsl.l) || 0; } hsl.l /= 2; if (hsl.s > 1) { hsl.s = 1; } return hsl; }, /** * Converts an HSL color value to RGB. Conversion formula * adapted from http://en.wikipedia.org/wiki/HSL_color_space. * Assumes s and l are contained in the set [0, 1] and h is * contained in the set [0, 360], returns r, g, and b in the * set [0, 255]. * * @param {number} h The hue * @param {number} s The saturation * @param {number} l The lightness * @return {Array} The RGB representation */ // hsl颜色转为rgb颜色 hsl2rgb(h, s, l) { h = h / 360; var r, g, b; if (s == 0) { r = g = b = l; // achromatic } else { var hue2rgb = function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; } var q = l < 0.5 ? l * (1 + s) : l + s - l * s; var p = 2 * l - q; r = hue2rgb(p, q, h + 1 / 3); g = hue2rgb(p, q, h); b = hue2rgb(p, q, h - 1 / 3); } return {r, g, b}; }, // rgb颜色转为hex颜色 rgb2hex(r, g, b) { return "#" + (16777216 | (b * 255) | ((g * 255) << 8) | ((r * 255) << 16)).toString(16).slice(1); } } } class DragContext { constructor({ $context, // 可以拖动的范围,即色盘以及滑块槽 $dragger, // 可拖动对象,色盘游标以及滑块 name, // 用来区分两个滑块 direction // 用来区分仅x轴还是x,y轴均可拖动 }) { this.$context = $context; this.$dragger = $dragger; this.name = name; this.direction = direction; this._isDragging = false; this._x = 0; this._y = 0; this._rect = this.$context.getBoundingClientRect(); } init() { this._addMousedown(); this._addMousemove(); this._addMouseup(); } _addMousedown() { utils.addHandler(this.$context, 'mousedown', (e) => { // 初始化样式 this._setStyles(e); this._isDragging = true; }); } _addMousemove() { utils.addHandler(document, 'mousemove', (e) => { if (this._isDragging) { this._setStyles(e); } }); } _addMouseup() { utils.addHandler(document, 'mouseup', (e) => { this._isDragging = false; }); } _setStyles(e) { this._setDraggerStyles(e); this._setContextStyles(e); } _setDraggerStyles(e) { // 设置dragger样式 this._x = utils.clamp(e.clientX - this._rect.left, 0, this._rect.width); this._y = utils.clamp(e.clientY - this._rect.top, 0, this._rect.height); switch (this.direction) { case 'horizontal': this.$dragger.style.transform = `translate(${this._x}px, 0)`; break; case 'vertical': this.$dragger.style.transform = `translate(0, ${this._y}px)`; break; case 'both': this.$dragger.style.transform = `translate(${this._x}px, ${this._y}px)`; } } _setContextStyles(e) { // 设置context样式 } } const $palletes = Array.prototype.slice.call(document.querySelectorAll('.palette')); const $sliders = Array.prototype.slice.call(document.querySelectorAll('.slider')); const $picker = document.querySelector('.color-picker'); const $indicator = document.querySelector('.color-indicator'); const contexts = $palletes.map(($context) => { const name = $context.getAttribute('name'); const $dragger = $sliders.filter(element => element.getAttribute('name') === name)[0]; return new DragContext({ $context, $dragger, name, direction: 'horizontal', initX: 120 }); }); contexts.forEach((context) => { context.init(); }); const name = $picker.getAttribute('name'); const context = new DragContext({ $context: $picker, $dragger: $indicator, name, direction: 'both' }); context.init();
<div class="wrapper"> <div class="color-picker" name="color"> <div class="color-indicator" name="color"></div> </div> <div class="preview"></div> <div class="hue palette" name="hue"> <div class="slider" name="hue"></div> </div> <div class="alpha palette" name="alpha"> <div class="slider" name="alpha"></div> </div> <h5 class="hex result">#fff</h5> <h5 class="rgb result">rgba(255, 255, 255, 1)</h5> <h5 class="hsl result">hsla(0, 0%, 100%, 1)</h5> </div> <div class="target">Change my color pls!</div>
@import url(https://fonts.googleapis.com/css?family=Quicksand); html, body { margin: 0; box-sizing: border-box; } body { background-color: #333; font-family: 'Quicksand', sans-serif; padding-top: 20px; } .wrapper { position: relative; background: white; margin: 0 auto; width: 200px; height: 265px; border-radius: 3px; border: 1px solid #666; } .wrapper::after { content: ''; position: absolute; width: 10px; height: 10px; top: 260px; left: 94px; background: linear-gradient(135deg, transparent 50%, white 50%); border-width: 1px 1px 0 0 inherit solid; transform: rotate(45deg); } .color-picker { width: 200px; height: 120px; margin-bottom: 15px; border-radius: inherit; background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1)), linear-gradient(to left, rgba(0, 0, 0, 0), rgba(255, 255, 255, 1)); background-color: red; overflow: hidden; } .color-indicator { position: relative; left: -6px; top: -6px; width: 12px; height: 12px; transform: translate(-6px, -6px); border-radius: 50%; border: 1px solid white; } .preview { background: linear-gradient(45deg, rgba(0,0,0,.25) 25%, transparent 0, transparent 75%, rgba(0,0,0,.25) 0) 0 0 / 12px 12px, linear-gradient(45deg, rgba(0,0,0,.25) 25%, transparent 0, transparent 75%, rgba(0,0,0,.25) 0) 6px 6px / 12px 12px; background-color: rgba(255,0,0,0.5); float: left; width: 32px; height: 32px; border-radius: 50%; margin-left: 12px; border: 1px solid #eee; } .palette { background: darkblue; width: 120px; height: 12px; margin-left: 60px; position: relative; border-radius: 1px; margin-bottom: 10px; } .hue { background: linear-gradient(to left, hsl(0, 100%, 50%) 0%, hsl(30, 100%, 50%) 8.33%, hsl(60, 100%, 50%) 16.67%, hsl(90, 100%, 50%) 25%, hsl(120, 100%, 50%) 33.33%, hsl(150, 100%, 50%) 41.67%, hsl(180, 100%, 50%) 50%, hsl(210, 100%, 50%) 58.33%, hsl(240, 100%, 50%) 66.67%, hsl(270, 100%, 50%) 75%, hsl(300, 100%, 50%) 83.33%, hsl(330, 100%, 50%) 91.67%, hsl(0, 100%, 50%) 100%); } .alpha { background: linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,255,1)) 0 0 / cover, linear-gradient(45deg, rgba(0,0,0,.25) 25%, transparent 0, transparent 75%, rgba(0,0,0,.25) 0) 0 0 / 12px 12px, linear-gradient(45deg, rgba(0,0,0,.25) 25%, transparent 0, transparent 75%, rgba(0,0,0,.25) 0) 6px 6px / 12px 12px; } .slider { width: 16px; height: 16px; border-radius: 50%; transform: translateX(120px); background: rgb(248, 248, 248); position: relative; left: -8px; top: -2px; filter: drop-shadow(2px 2px 3px rgba(0,0,0,.2)); } .result { color: #aaa; margin: 0; line-height: 2; text-align: center; user-select: auto; } .target { width: max-content; margin: 0 auto; padding-top: 10px; color: white; font-size: 24px; cursor: pointer; }