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 相关文章推荐
javascript 命名空间以提高代码重用性
Nov 13 Javascript
使用javascript:将其它类型值转换成布尔类型值的解决方法详解
May 07 Javascript
Jquery中使用show()与hide()方法动画显示和隐藏图片
Oct 08 Javascript
如何实现移动端浏览器不显示 pc 端的广告
Oct 15 Javascript
微信小程序 实现动态显示和隐藏某个控件
Apr 27 Javascript
vue项目中的webpack-dev-sever配置方法
Dec 14 Javascript
javascript原生封装一个淡入淡出效果的函数测试实例代码
Mar 19 Javascript
vue路由事件beforeRouteLeave及组件内定时器的清除方法
Sep 29 Javascript
基于jquery实现九宫格拼图小游戏
Nov 30 jQuery
如何让node运行es6模块文件及其原理详解
Dec 11 Javascript
Vue-cli项目部署到Nginx服务器的方法
Nov 01 Javascript
vue和小程序项目中使用iconfont的方法
May 19 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木马攻击防御之道
2008/03/24 PHP
php通过COM类调用组件的实现代码
2012/01/11 PHP
关于crontab的使用详解
2013/06/24 PHP
PHP实现的简单排列组合算法应用示例
2017/06/20 PHP
javascript编程起步(第六课)
2007/01/10 Javascript
jQuery 版本的文本输入框检查器Input Check
2009/07/09 Javascript
JavaScript高级程序设计 阅读笔记(十七) js事件
2012/08/14 Javascript
js获取事件源及触发该事件的对象
2013/10/24 Javascript
Jquery通过Ajax访问XML数据的小例子
2013/11/18 Javascript
jQuery表格排序组件-tablesorter使用示例
2014/05/26 Javascript
Node.js实现文件上传
2016/07/05 Javascript
JavaScript 总结几个提高性能知识点(推荐)
2017/02/20 Javascript
[js高手之路]图解javascript的原型(prototype)对象,原型链实例
2017/08/28 Javascript
Vue组件和Route的生命周期实例详解
2018/02/10 Javascript
vue.js2.0点击获取自己的属性和jquery方法
2018/02/23 jQuery
vue router+vuex实现首页登录验证判断逻辑
2018/05/17 Javascript
JS正则表达式常见用法实例详解
2018/06/19 Javascript
微信小程序之事件交互操作实例分析
2018/12/03 Javascript
微信小程序+云开发实现欢迎登录注册
2019/05/24 Javascript
用JS实现一个简单的打砖块游戏
2019/12/11 Javascript
uni-app如何实现增量更新功能
2020/01/03 Javascript
javascript 原型与原型链的理解及应用实例分析
2020/02/10 Javascript
跟老齐学Python之有容乃大的list(1)
2014/09/14 Python
python自动化生成IOS的图标
2018/11/13 Python
Python生成指定数量的优惠码实操内容
2019/06/18 Python
使用 python pyautogui实现鼠标键盘控制功能
2019/08/04 Python
Python实现串口通信(pyserial)过程解析
2019/09/25 Python
Tensorflow安装问题: Could not find a version that satisfies the requirement tensorflow
2020/04/20 Python
python+django+selenium搭建简易自动化测试
2020/08/19 Python
ALEX AND ANI:手镯,项链,耳环和更多
2017/04/20 全球购物
JMS中Topic和Queue有什么区别
2013/05/15 面试题
Linux Interview Questions For software testers
2012/06/02 面试题
业务代表的岗位职责
2013/11/16 职场文书
《中华少年》教学反思
2014/02/15 职场文书
关爱女孩行动实施方案
2014/03/13 职场文书
Python多线程实用方法以及共享变量资源竞争问题
2022/04/12 Python