js实现拾色器插件(ColorPicker)


Posted in Javascript onMay 21, 2020

对一个前端来说,颜色选择的插件肯定不陌生,许多小伙伴对这类插件的实现可能会比较好奇,这里奉上原生js版本的拾色器。

效果图:

js实现拾色器插件(ColorPicker)

讲下实现方式:

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) {};

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery利用event.which方法获取键盘输入值的代码
Oct 09 Javascript
javascript中的void运算符语法及使用介绍
Mar 10 Javascript
Js nodeType 属性全面解析
Nov 14 Javascript
AngularJS中取消对HTML片段转义的方法例子
Jan 04 Javascript
javascript实现点击按钮让DIV层弹性移动的方法
Feb 24 Javascript
基于jQuery实现鼠标点击导航菜单水波动画效果附源码下载
Jan 06 Javascript
canvas绘图不清晰的解决方案
Feb 28 Javascript
原生JS+Canvas实现五子棋游戏实例
Jun 19 Javascript
JS实现获取汉字首字母拼音、全拼音及混拼音的方法
Nov 14 Javascript
easyui下拉框动态级联加载的示例代码
Nov 29 Javascript
vue click.stop阻止点击事件继续传播的方法
Sep 04 Javascript
JavaScript实现抖音罗盘时钟
Oct 11 Javascript
原生js实现日期选择插件
May 21 #Javascript
vue+Element中table表格实现可编辑(select下拉框)
May 21 #Javascript
浅谈React中组件逻辑复用的那些事儿
May 21 #Javascript
记一次用ts+vuecli4重构项目的实现
May 21 #Javascript
JS实现图片幻灯片效果代码实例
May 21 #Javascript
Javascript实现秒表计时游戏
May 27 #Javascript
JavaScript实现猜数字游戏
May 20 #Javascript
You might like
php中对xml读取的相关函数的介绍一
2008/06/05 PHP
PHP获取短链接跳转后的真实地址和响应头信息的方法
2014/07/25 PHP
Zend Framework教程之Loader以及PluginLoader用法详解
2016/03/09 PHP
php使用pclzip类实现文件压缩的方法(附pclzip类下载地址)
2016/04/30 PHP
PHP 面向对象程序设计之类属性与类常量实现方法分析
2020/04/13 PHP
js变量作用域及可访问性的探讨
2006/11/23 Javascript
javascript让setInteval里的函数参数中的this指向特定的对象
2010/01/31 Javascript
jquery uaMatch源代码
2011/02/14 Javascript
javascript显示用户停留时间的简单实例
2013/08/05 Javascript
jQuery制作仿腾讯web qq用户体验桌面
2013/08/20 Javascript
iframe实用操作锦集
2014/04/22 Javascript
javascript中Number对象的toString()方法分析
2014/12/20 Javascript
js获取及判断键盘按键的方法
2015/12/01 Javascript
浅析Node.js实现HTTP文件下载
2016/08/05 Javascript
bootstrapValidator 重新启用提交按钮的方法
2017/02/20 Javascript
详解webpack es6 to es5支持配置
2017/05/04 Javascript
详解AngularJS1.x学习directive 中‘&amp; ’‘=’ ‘@’符号的区别使用
2017/08/23 Javascript
Vue网页html转换PDF(最低兼容ie10)的思路详解
2017/08/24 Javascript
webpack引入eslint配置详解
2018/01/22 Javascript
js中addEventListener()与removeEventListener()用法案例分析
2020/03/02 Javascript
基于vue3.0.1beta搭建仿京东的电商H5项目
2020/05/06 Javascript
[02:10]三分钟回顾完美世界城市挑战赛
2019/01/24 DOTA
python正则表达式re模块详解
2014/06/25 Python
python 内置函数filter
2017/06/01 Python
Python2实现的LED大数字显示效果示例
2017/09/04 Python
Python时间戳使用和相互转换详解
2017/12/11 Python
通过Python实现一个简单的html页面
2020/05/16 Python
Python实现我的世界小游戏源代码
2021/03/02 Python
美国二手奢侈品寄售网站:TheRealReal
2016/10/29 全球购物
澳大利亚制造的蜡烛和扩散器:Glasshouse Fragrances
2018/05/20 全球购物
一套SQL笔试题
2016/08/14 面试题
主题酒店策划书
2014/01/28 职场文书
给校长的建议书500字
2014/05/15 职场文书
医学求职信
2014/05/28 职场文书
艺术学院毕业生求职信
2014/07/09 职场文书
熟背这些句子,让您的英语口语突飞猛进(135句)
2019/09/06 职场文书