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 火狐(firefox)不显示本地图片问题解决
Jul 05 Javascript
有关DOM元素与事件的3个谜题
Nov 11 Javascript
JS.findElementById()使用介绍
Sep 21 Javascript
js控制页面控件隐藏显示的两种方法介绍
Oct 09 Javascript
Javascript中匿名函数的调用与写法实例详解(多种)
Jan 26 Javascript
javascript实现可键盘控制的抽奖系统
Mar 10 Javascript
浅谈jquery上下滑动的注意事项
Oct 13 Javascript
jQuery实现百度图片移入移出内容提示框上下左右移动的效果
Jun 05 jQuery
浅谈 Webpack 如何处理图片(开发、打包、优化)
May 15 Javascript
JS实现简易计算器
Feb 14 Javascript
Vue.js 带下拉选项的输入框(Textbox with Dropdown)组件
Apr 17 Vue.js
JavaScript实现班级抽签小程序
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 批量删除数据的方法分析
2009/10/30 PHP
腾讯QQ php程序员面试题目整理
2010/06/08 PHP
测试php函数的方法
2013/11/13 PHP
Yii rules常用规则示例
2016/03/15 PHP
php 截取中英文混合字符串的方法
2018/05/31 PHP
PHP mongodb操作类定义与用法示例【适合mongodb2.x和mongodb3.x】
2018/06/16 PHP
Laravel框架实现文件上传的方法分析
2019/09/29 PHP
js解析xml字符串和xml文档实现原理及代码(针对ie与火狐)
2013/02/02 Javascript
js模仿jquery的写法示例代码
2013/06/16 Javascript
JavaScript实现数字数组按照倒序排列的方法
2015/04/06 Javascript
简介AngularJS的视图功能应用
2015/06/17 Javascript
简述Jquery与DOM对象
2015/07/10 Javascript
浅析AngularJS Filter用法
2015/12/28 Javascript
详解js数组的完全随机排列算法
2016/12/16 Javascript
bootstrap选项卡使用方法解析
2017/01/11 Javascript
iframe高度自适应及隐藏滚动条的实例详解
2017/09/29 Javascript
nodejs微信开发之授权登录+获取用户信息
2019/03/17 NodeJs
详解jQuery设置内容和属性
2019/04/11 jQuery
用js简单提供增删改查接口
2019/05/12 Javascript
基于Vue SEO的四种方案(小结)
2019/07/01 Javascript
js实现指定时间倒计时效果
2019/08/26 Javascript
Python列表(list)常用操作方法小结
2015/02/02 Python
Python中的MongoDB基本操作:连接、查询实例
2015/02/13 Python
python轻松实现代码编码格式转换
2015/03/26 Python
Python使用xlrd模块操作Excel数据导入的方法
2015/05/26 Python
基于进程内通讯的python聊天室实现方法
2015/06/28 Python
详解Python的hasattr() getattr() setattr() 函数使用方法
2018/07/09 Python
仿酷狗html5手机音乐播放器主要部分代码
2013/05/15 HTML / CSS
机关门卫的岗位职责
2014/04/29 职场文书
2014年教师党员公开承诺书
2014/05/28 职场文书
大学团日活动新闻稿
2014/09/10 职场文书
授权委托书(法人单位用)
2014/09/29 职场文书
2014年图书室工作总结
2014/12/09 职场文书
老员工辞职信范文
2015/05/12 职场文书
Filebeat 采集 Nginx 日志的方法
2021/03/31 Servers
教你如何使用Python开发一个钉钉群应答机器人
2021/06/21 Python