js實現拾色器外掛(ColorPicker)
阿新 • • 發佈:2020-05-22
對一個前端來說,顏色選擇的外掛肯定不陌生,許多小夥伴對這類外掛的實現可能會比較好奇。這裡奉上原生js版本的拾色器,由於是本人純手工擼出來的,所以轉載還請標明來源。
效果圖:
講下實現方式:
1.顏色除了RGB跟十六進位制的表現外,還有一個HSV的表現形式。H(hue)是色相,值域是0度到360度,這個值控制的是你看到的是什麼顏色,通俗點講就是紅橙黃綠...;S(saturation)是飽和度,值域是0到1,這個值控制顏色的鮮豔程度,可以理解為大紅跟淡紅的差別;V(value)可以理解為亮度,值域也是0到1。
2.rgb顏色跟hsv顏色的相互轉化有專門的公式,可自行去百度瞭解下
3.面向物件的程式設計方式公認為易擴充套件,高複用。
整個目錄結構如下:
COLORPICKER
--css
--common.css(樣式)
--js
--colorPicker.js(外掛主體)
--event.js(簡易的釋出者-訂閱者實現)
--inherite.js(繼承手段,寄生組合式)
ColorPicker.html
使用說明:
外掛目前只支援傳入h、s、v值來初始化顏色,若什麼都不傳,預設h、s、v都為0,例項化ColorPicker建構函式後,通過select方法初始化;目前只暴露了兩個回撥介面onHChange(色相改變觸發)、onSVChange(飽和度或亮度改變觸發):
var aa = new ColorPicker(); aa.select( // { // h: 120,// s: 1,// b: 1 // } ); aa.onHChange = function(e) {}; aa.onSVChange = function(e) {};
程式碼如下:
ColorPicker.html:
<!DOCTYPE html> <!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]--> <!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]--> <!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]--> <!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]--> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Color Picker</title> <meta name="description" content=""> <meta name="viewport" content="width=device-width,initial-scale=1"> <link rel="stylesheet" href="./css/common.css" rel="external nofollow" > </head> <body> </body> <script type="text/javascript" src="js/event.js"></script> <script type="text/javascript" src="js/inherite.js"></script> <script type="text/javascript" src="js/colorPicker.js"></script> </html>
common.css:
body { height: calc(100vh); overflow: hidden; background: gray; } .color-picker-container { border: 0; width: 300px; margin: 0 auto; } .color-picker-container .val-container { border: 1px solid silver; text-align: center; line-height: 30px; font-weight: bold; } .color-picker-container .val-container .val { width: 100%; border: 0; line-height: 32px; text-align: center; font-weight: bold; } .color-picker-container .picker-container { position: relative; width: 100%; margin-top: 15px; } .color-picker-container .picker-container .pointer { position: absolute; width: 14px; height: 14px; border-radius: 50%; border: 3px solid #FFFFFF; margin-left: -9px; margin-top: -9px; top: 255px; left: 0; cursor: pointer; } .color-picker-container .picker-container .saturation-range { float: left; width: 255px; height: 255px; /* background-color: rgba(0,.2); */ box-shadow: 1px 1px 5px rgba(0,.6); } .color-picker-container .picker-container .saturation-range .cover { width: 100%; height: 100%; background: -webkit-linear-gradient(top,rgb(0,0) 0%,0) 100%); background: linear-gradient(top,0) 100%); } .color-picker-container .picker-container .hue-range { float: right; width: 30px; height: 255px; box-shadow: 1px 1px 5px rgba(0,.6); background: -webkit-linear-gradient(top,rgb(255,255) 17%,255) 34%,255,255) 50%,0) 67%,0) 84%,0) 100%); } .color-picker-container .picker-container .hue-range .cursor { position: relative; width: 44px; margin-top: -11px; top: 255px; cursor: n-resize; } .color-picker-container .picker-container .hue-range .cursor::before { content: ''; display: inline-block; width: 0; height: 0; border-style: solid; border-top-width: 5px; border-right-width: 0; border-bottom-width: 5px; border-left-width: 7px; border-top-color: transparent; border-bottom-color: transparent; border-left-color: rgba(0,.5); margin-left: -8px; } .color-picker-container .picker-container .hue-range .cursor::after { content: ''; display: inline-block; width: 0; height: 0; border-style: solid; border-top-width: 5px; border-right-width: 7px; border-bottom-width: 5px; border-left-width: 0; border-top-color: transparent; border-bottom-color: transparent; border-right-color: rgba(0,.5); margin-left: 33px; } .color-picker-container .picker-container::after { content: ''; display: block; clear: both; line-height: 0; visibility: hidden; }
event.js:
function Event() { this.bindEvent = []; } Event.prototype.addEvent = function(name,callback) { if(typeof callback !== 'function') return; var bExistEvent = false; var untieEvent = function() { if(window.removeEventListener) { this.element.removeEventListener(name,callback); } else if(window.detachEvent) { this.element.detachEvent('on' + name,callback); } else { this.element['on' + name] = null; } }; for(var i = 0,len = this.bindEvent.length; i < len; i++) { if(this.bindEvent[i].name == name) { this.removeEvent(name); this.bindEvent[i].untie = untieEvent; this.bindEvent[i].event = callback; bExistEvent = true; break; } } if(window.addEventListener) { this.element.addEventListener(name,callback); } else if(window.attachEvent) { this.element.attachEvent('on' + name,callback); } else { this.element['on' + name] = callback; } if(!bExistEvent) { this.bindEvent.push({ name: name,event: callback,untie: function() { if(window.removeEventListener) { this.element.removeEventListener(name,callback); } else if(window.detachEvent) { this.element.detachEvent('on' + name,callback); } else { this.element['on' + name] = null; } } }); } } Event.prototype.removeEvent = function(name) { if(typeof name === 'undefined' || name === '') return; // 從已繫結事件列表中剔除 for(var i = 0,len = this.bindEvent.length; i < len; i++) { if(this.bindEvent[i].name == name) { this.bindEvent[i].untie.call(this); // 移除繫結事件 this.bindEvent.splice(i,1); // 從事件列表刪除 break; } } } Event.prototype.triggerEvent = function(name) { var callback = null; for(var i = 0,len = this.bindEvent.length; i < len; i++) { if(this.bindEvent[i].name === name) { callback = this.bindEvent[i].event; } } if(typeof callback === 'function') { callback.apply(this,[].slice.call(arguments).slice(1)); } }
inherite.js:
function inheritObj(o) { function F() {}; F.prototype = o; return new F(); } function inheritProto(subclass,supclass) { subclass.prototype = inheritObj(supclass.prototype); subclass.prototype.constructor = subclass; } function extend(p,o) { for(var item in o) { if(!p.hasOwnProperty(item)) { p[item] = o[item]; } } } function Container(className) { this.element = null; this.className = className || ''; this.parent = null; this.children = []; this.init(); } Container.prototype.init = function() { this.element = document.createElement(this.tagname || 'div'); this.className && (this.element.className = this.className); } Container.prototype.getElement = function() { return this.element; } Container.prototype.add = function(item) { item.parent = this; this.children.push(item); this.element.appendChild(item.getElement()); return this; } function Item(className,tagname) { this.tagname = tagname || 'div'; Container.call(this,className); delete this.children; } inheritProto(Item,Container); Item.prototype.add = function() { throw new Error('[[Type Item]] can not add any other item'); }
colorPicker.js:
(function() { this.ColorPicker = function() { Container.call(this); this.hsv = [0,0]; this.rgb = [0,0]; this.svFieldHsv = [0,1,1]; this.svFieldRgb = [0,0]; } inheritProto(ColorPicker,Container); ColorPicker.prototype.init = function() { this.element = document.createElement('div'); this.element.className = 'color-picker-container'; var _container = createContainer(),_self = this; Event.call(_container); extend(_container,Event.prototype); createVal.call(this); createSV.call(_container); createH.call(_container); _container.addEvent('sv-change',function(e) { // 暴露出飽和度change介面 _self.onSVChange(e); }); _container.addEvent('h-change',function(e) { // 暴露出色相change介面 _self.onHChange(e); }); this.add(_container); } ColorPicker.prototype.select = function(opt) { if(opt && typeof opt !== 'undefined') { this.hsv[0] = opt.h || 0; this.hsv[1] = opt.s || 0; this.hsv[2] = opt.b || 0; this.svFieldHsv[0] = opt.hue || 0; } if(this.children[0].children[0].getElement().value !== '') { var val = this.children[0].children[0].getElement().value,regRgb = /rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/,r = val.replace(regRgb,'$1'),g = val.replace(regRgb,'$2'),b = val.replace(regRgb,'$3'); this.hsv = rgb2hsv(r,g,b); this.svFieldHsv[0] = this.hsv[0]; } this.svFieldRgb = hsv2rgb(this.svFieldHsv[0],this.svFieldHsv[1],this.svFieldHsv[2]); this.updateSVField(); this.rgb = hsv2rgb(this.hsv[0],this.hsv[1],this.hsv[2]); this.updateSVPointer(); this.updateVal(opt); this.children[1].children[0].getElement().style.cssText += ';left: ' + this.hsv[1] * 255 + 'px;top: ' + (255 - this.hsv[2] * 255) + 'px;'; this.children[1].children[2].children[0].getElement().style.cssText += ';top: ' + (255 - this.hsv[0]) + 'px;'; document.body.appendChild(this.element); return this; } ColorPicker.prototype.updateSVField = function() { this.children[1].children[1].getElement().style.cssText = ';background: -webkit-linear-gradient(left,255) 0%,rgb(' + ~~this.svFieldRgb[0] + ',' + ~~this.svFieldRgb[1] + ',' + ~~this.svFieldRgb[2] + ') 100%)'; } ColorPicker.prototype.updateSVPointer = function() { this.children[1].children[0].getElement().style.cssText += ';background-color: rgb(' + ~~this.rgb[0] + ',' + ~~this.rgb[1] + ',' + ~~this.rgb[2] + ')'; } ColorPicker.prototype.updateVal = function() { var _hsv_temp = [0,0]; if(this.hsv[1] < 0.5 && this.hsv[2] > 0.5) { _hsv_temp = [this.hsv[0],0]; } else { _hsv_temp = [this.hsv[0],1]; } var _rgb_temp = hsv2rgb(_hsv_temp[0],_hsv_temp[1],_hsv_temp[2]); this.children[0].children[0].getElement().style.cssText += ';text-shadow: 0 0 5px;color: rgb(' + ~~_rgb_temp[0] + ',' + ~~_rgb_temp[1] + ',' + ~~_rgb_temp[2] + ');background-color: rgb(' + ~~this.rgb[0] + ',' + ~~this.rgb[2] + ')'; this.children[1].children[0].getElement().style.cssText += ';border-color: rgb(' + ~~_rgb_temp[0] + ',' + ~~_rgb_temp[2] + ')'; this.children[0].children[0].getElement().value = 'rgb(' + ~~this.rgb[0] + ',' + ~~this.rgb[2] + ')'; } ColorPicker.prototype.onSVChange = function(callVal) { arguments.callee.call(this,callVal); return this; } ColorPicker.prototype.onHChange = function(callVal) { arguments.callee.call(this,callVal); return this; } function createVal() { var _container = new Container('val-container'),_input = new Item('val','input'); Event.call(_input); extend(_input,Event.prototype); _input.addEvent('blur',this.select.bind(this)); _container.add(_input); this.add(_container); } function createContainer() { var _container = new Container('picker-container'); return _container; } function createSV() { var _pointer = new Item('pointer'),_saturationRange = new Container('saturation-range'),_cover = new Item('cover'),_self = this; Event.call(_pointer); extend(_pointer,Event.prototype); _saturationRange.add(_cover); function cursorDown(e) { var _top = typeof e.target.style.top === 'undefined' ? 255 : parseFloat(e.target.style.top),_left = typeof e.target.style.left === 'undefined' ? 0 : parseFloat(e.target.style.left),_distanceY,_distanceX,realTop,realLeft; function move(e2) { _distanceY = e2.clientY - e.clientY; _distanceX = e2.clientX - e.clientX; realTop = _top + _distanceY; realLeft = _left + _distanceX; realTop < 0 && (realTop = 0); realTop > 255 && (realTop = 255); realLeft < 0 && (realLeft = 0); realLeft > 255 && (realLeft = 255); e.target.style.top = realTop + 'px'; e.target.style.left = realLeft + 'px'; _self.parent.hsv[1] = realLeft / 255; _self.parent.hsv[2] = (255 - realTop) / 255; _self.parent.rgb = hsv2rgb(_self.parent.hsv[0],_self.parent.hsv[1],_self.parent.hsv[2]); _self.parent.updateSVPointer(); _self.parent.updateVal(); } function up() { document.removeEventListener('mousemove',move); document.removeEventListener('mouseup',up); _self.triggerEvent('sv-change',[realLeft / 255,(255 - realTop) / 255]); } document.addEventListener('mousemove',move); document.addEventListener('mouseup',up); } _pointer.addEvent('mousedown',cursorDown); this.add(_pointer) .add(_saturationRange); } function createH() { var _hueRange = new Container('hue-range'),_cursor = new Item('cursor'),_self = this; Event.call(_cursor); extend(_cursor,Event.prototype); function cursorDown(e) { var _top = typeof e.target.style.top === 'undefined' ? 255 : parseFloat(e.target.style.top),_distance,realTop; function move(e2) { _distance = e2.clientY - e.clientY; realTop = _top + _distance; realTop < 0 && (realTop = 0); realTop > 255 && (realTop = 255); e.target.style.top = realTop + 'px'; _self.parent.svFieldHsv[0] = 255 - realTop; _self.parent.svFieldRgb = hsv2rgb(_self.parent.svFieldHsv[0],_self.parent.svFieldHsv[1],_self.parent.svFieldHsv[2]); _self.parent.updateSVField(); _self.parent.hsv[0] = 255 - realTop; _self.parent.rgb = hsv2rgb(_self.parent.hsv[0],up); _self.triggerEvent('h-change',255 - realTop); } document.addEventListener('mousemove',up); } _cursor.addEvent('mousedown',cursorDown); _hueRange.add(_cursor); this.add(_hueRange); } function hsv2rgb(h,s,v) { h = h / 255 * 360; var hi = Math.floor(h / 60) % 6,f = h / 60 - Math.floor(h / 60),p = v * (1 - s),q = v * (1 - f * s),t = v * (1 - (1 - f) * s),c = [ [v,t,p],[q,v,[p,t],q,v],[t,p,[v,q] ][hi]; return [c[0] * 255,c[1] * 255,c[2] * 255]; } function rgb2hsv(r,b) { var max = Math.max(r,b),min = Math.min(r,h,d = max - min; if (max == min) { h = 0; } else if (max == r) { h = 60 * ((g - b) / d); } else if (max == g) { h = 60 * ((b - r) / d) + 120; } else { h = 60 * ((r - g) / d) + 240; } s = max == 0 ? 0 : (1 - min / max); v = max; if(h < 0) { h += 360; } return [h * 255 / 360,v / 255]; } })() var aa = new ColorPicker(); aa.select( // { // h: 120,// b: 1 // } ); aa.onHChange = function(e) {}; aa.onSVChange = function(e) {};
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。