js实现拾色器插件(ColorPicker)

对一个前端来说,颜色选择的插件肯定不陌生,许多小伙伴对这类插件的实现可能会比较好奇,这里奉上原生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, 0, 0, .2); */

box-shadow: 1px 1px 5px rgba(0, 0, 0, .6);

}

.color-picker-container .picker-container .saturation-range .cover {

width: 100%;

height: 100%;

background: -webkit-linear-gradient(top, rgb(0, 0, 0, 0) 0%, rgb(0, 0, 0) 100%);

background: linear-gradient(top, rgb(0, 0, 0, 0) 0%, rgb(0, 0, 0) 100%);

}

.color-picker-container .picker-container .hue-range {

float: right;

width: 30px;

height: 255px;

box-shadow: 1px 1px 5px rgba(0, 0, 0, .6);

background: -webkit-linear-gradient(top,

rgb(255, 0, 0) 0%,

rgb(255, 0, 255) 17%,

rgb(0, 0, 255) 34%,

rgb(0, 255, 255) 50%,

rgb(0, 255, 0) 67%,

rgb(255, 255, 0) 84%,

rgb(255, 0, 0) 100%);

background: linear-gradient(top,

rgb(255, 0, 0) 0%,

rgb(255, 0, 255) 17%,

rgb(0, 0, 255) 34%,

rgb(0, 255, 255) 50%,

rgb(0, 255, 0) 67%,

rgb(255, 255, 0) 84%,

rgb(255, 0, 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, 0, 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, 0, 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, 0];

this.rgb = [0, 0, 0];

this.svFieldHsv = [0, 1, 1];

this.svFieldRgb = [0, 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, rgb(255, 255, 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, 0];

if(this.hsv[1] < 0.5 && this.hsv[2] > 0.5) {

_hsv_temp = [this.hsv[0], 1, 0];

} else {

_hsv_temp = [this.hsv[0], 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[1] + ', ' + ~~this.rgb[2] + ')';

this.children[1].children[0].getElement().style.cssText += ';border-color: rgb(' + ~~_rgb_temp[0] + ', ' + ~~_rgb_temp[1] + ', ' + ~~_rgb_temp[2] + ')';

this.children[0].children[0].getElement().value = 'rgb(' + ~~this.rgb[0] + ', ' + ~~this.rgb[1] + ', ' + ~~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], _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('h-change', 255 - realTop);

}

document.addEventListener('mousemove', move);

document.addEventListener('mouseup', 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],

[p, v, t],

[p, q, v],

[t, p, v],

[v, p, q]

][hi];

return [c[0] * 255, c[1] * 255, c[2] * 255];

}

function rgb2hsv(r, g, b) {

var max = Math.max(r, g, b),

min = Math.min(r, g, b),

h, s, v,

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, s, v / 255];

}

})()

var aa = new ColorPicker();

aa.select(

// {

// h: 120,

// s: 1,

// b: 1

// }

);

aa.onHChange = function(e) {};

aa.onSVChange = function(e) {};

以上是 js实现拾色器插件(ColorPicker) 的全部内容, 来源链接: utcz.com/z/353727.html

回到顶部