TheRogerLAB Codes

Color picker UI component

  July, 2021

Introduction

In this post we will create a color picker UI component using only vanilla javascript. Check the following code and try the demo at the end. Feel free to use it and improve it.

color picker component

In this example we will be using our UI component template. You can find it here: Create an UI component in javascript

Step 1: Let's define our object template

var colorpicker_lab = {
 items: new Array(),

 cmp: function(id){
  return this.items[id];
 },

 component: function(id){
  /****** REPLACE THIS LINE WITH STEP 2... ******/
  /****** REPLACE THIS LINE WITH STEP 3... ******/
  /****** REPLACE THIS LINE WITH STEP 4... ******/
  /****** REPLACE THIS LINE WITH STEP 5... ******/
  /****** REPLACE THIS LINE WITH STEP 6... ******/
  /****** REPLACE THIS LINE WITH STEP 7... ******/
 },

 create:function (id) {
  this.items[id] = new this.component(id);
  return this.items[id];
 }
};

Step 2: Define the configuration object and functions

//config object default
this.config = {
 type: 'hex',
 ancho: 145,
 opacityVal: 1,
 per: 100/6,

 arrPor:[ //colors percents
 100/6*0,
 100/6*1,
 100/6*2,
 100/6*3,
 100/6*4,
 100/6*5,
 100/6*6
 ],

 arrCol:[//colors values
 [255,0,0],
 [255,255,0],
 [0,255,0],
 [0,255,255],
 [0,0,255],
 [255,0,255],
 [255,0,0]
 ],

 //for save button event
 onSaveEl:function(){},

 //for cancel button event
 onCancelEl:function(){}
},

//object for override config object just with changed default properties
this.settings = {},

//function for override config with setting
this.matchSettings = function(){
  var keys = [];
  for (var k in this.settings){
    if(k in this.config)
      this.config[k] = this.settings[k];
    }
},

//slots object just in case
this.slots = {
  content:""
},

Step 3: Define functions for direct color selector

color picker direct color
Fig. - Direct color selector.
//For painting direct color selector
this.setPrimaryDefault = function(){

//white
 document.querySelector("#prim1_"+id).style.background = 'rgba(255,255,255,'+this.config.opacityVal+')';
 document.querySelector("#prim1_"+id).style.border = '1px solid rgba(255,255,255,'+this.config.opacityVal+')';

//red
 document.querySelector("#prim2_"+id).style.background = 'rgba(255,0,0,'+this.config.opacityVal+')';
 document.querySelector("#prim2_"+id).style.border = '1px solid rgba(255,0,0,'+this.config.opacityVal+')';

//yellow
 document.querySelector("#prim3_"+id).style.background = 'rgba(255,255,0,'+this.config.opacityVal+')';
 document.querySelector("#prim3_"+id).style.border = '1px solid rgba(255,255,0,'+this.config.opacityVal+')';

//green
 document.querySelector("#prim4_"+id).style.background = 'rgba(0,255,0,'+this.config.opacityVal+')';
 document.querySelector("#prim4_"+id).style.border = '1px solid rgba(0,255,0,'+this.config.opacityVal+')';

//black
 document.querySelector("#prim5_"+id).style.background = 'rgba(0,0,0,'+this.config.opacityVal+')';
 document.querySelector("#prim5_"+id).style.border = '1px solid rgba(191, 191, 191,'+this.config.opacityVal+')';

//cian
 document.querySelector("#prim6_"+id).style.background = 'rgba(0,255,255,'+this.config.opacityVal+')';
 document.querySelector("#prim6_"+id).style.border = '1px solid rgba(0,255,255,'+this.config.opacityVal+')';

//blue
 document.querySelector("#prim7_"+id).style.background = 'rgba(0,0,255,'+this.config.opacityVal+')';
 document.querySelector("#prim7_"+id).style.border = '1px solid rgba(0,0,255,'+this.config.opacityVal+')';

//magenta
 document.querySelector("#prim8_"+id).style.background = 'rgba(255,0,255,'+this.config.opacityVal+')';
 document.querySelector("#prim8_"+id).style.border = '1px solid rgba(255,0,255,'+this.config.opacityVal+')';

},

//For selecting direct color
this.selectPrimary = function(){
 var el = event.target;
 var color = getComputedStyle(el, null).getPropertyValue('background-color');

 document.querySelector('#demo1_'+id).innerHTML = `${color}`;
 document.querySelector('#sele_'+id).style.backgroundColor = `${color}`;

 //converting from rgba to hex
 var e = this.rgb2hex(`${color}`);
 var hex = e + this.alpha2hexa(this.config.opacityVal);
 document.querySelector('#demo2_'+id).innerHTML = hex;
},

Step 4: Define functions for opacity and color sliders

color picker, color and opacity sliders
Fig. - Opacity and color sliders.
//when slide on opacity slider
this.selectOpacity = function(){
 var currentValue = document.getElementById("myRangeOpacity_"+id).value;
 this.config.opacityVal = parseFloat(currentValue / 100);
 document.getElementById("myRange_"+id).oninput();
 this.setPrimaryDefault();
}

//when slide on the color slider
this.selectHue = function(){
 var currentValue = document.getElementById("myRange_"+id).value;
 var colorRange = [];

 if(currentValue>=this.config.per*0 && currentValue<=this.config.per*1)
  colorRange = [0,1];else
 if(currentValue>this.config.per*1 && currentValue<=this.config.per*2)
  colorRange = [1,2];else
 if(currentValue>this.config.per*2 && currentValue<=this.config.per*3)
  colorRange = [2,3];else
 if(currentValue>this.config.per*3 && currentValue<=this.config.per*4)
  colorRange = [3,4];else
 if(currentValue>this.config.per*4 && currentValue<=this.config.per*5)
  colorRange = [4,5];else
 if(currentValue>this.config.per*5 && currentValue<=this.config.per*6)
  colorRange = [5,6];

 var firstcolor = this.config.arrCol[colorRange[0]];
 var secondcolor = this.config.arrCol[colorRange[1]];

 //Calculate ratio between the two closest colors
 var firstcolor_x = this.config.ancho*(this.config.arrPor[colorRange[0]]/100);
 var secondcolor_x = this.config.ancho*(this.config.arrPor[colorRange[1]]/100)-firstcolor_x;
 var slider_x = this.config.ancho*(currentValue/100)-firstcolor_x;
 var ratio = slider_x/secondcolor_x;

 var result = this.pickHex( secondcolor,firstcolor, ratio );
 var colorNew = 'rgba('+result.join()+','+this.config.opacityVal+')';

//changing the color to the color canvas
 document.querySelector('#canva_'+id).style.background = `linear-gradient(to top, rgb(000), transparent) repeat scroll 00%, rgba(0000) linear-gradient(to left,`+colorNew+`, rgb(255255255)) repeat scroll 00%`;
 var hue = parseFloat(currentValue / 100);
 document.querySelector('#canva_'+id).setAttribute('data-hue',hue);

//painting the color
 this.drawCurrent();
},

//Paint the current selected color and its values
this.drawCurrent = function(){
 var palette = document.querySelector('#canva_'+id);
 var pin = palette.querySelector('#pin_'+id);

 var x = pin.dataset.left * 1;
 var y = pin.dataset.top * 1;

 const width = palette.clientWidth;
 const height = palette.clientHeight;

 const saturation = parseFloat(x / width);
 const value = parseFloat((height - y) / height);

 const hue = palette.dataset.hue || 0;
 alpha = this.config.opacityVal;

 //set the color
 const color = this.HSVtoRGB(hue, saturation, value);

 //setting on rgba label and selected color square
 document.querySelector('#demo1_'+id).innerHTML = `rgba(${color.r},${color.g},${color.b},${alpha})`;
 document.querySelector('#sele_'+id).style.backgroundColor = `rgba(${color.r},${color.g},${color.b},${alpha})`;

 //setting on temporal selected color square
 document.querySelector('#seleTemporal_'+id).style.backgroundColor = `rgba(${color.r},${color.g},${color.b},${alpha})`;

 //from rgba to hex
 var e = this.rgb2hex(`rgba(${color.r},${color.g},${color.b},${alpha})`);
 var hex = e + this.alpha2hexa(alpha);
 document.querySelector('#demo2_'+id).innerHTML = hex;

},

Step 5: Define functions for click and mouse over color canvas

color picker, color hue
Fig. - Color canvas and pin.
Fig. - Temporal and selected areas.
//click on canvas
this.selectColorCanva = function(){
 var palette = document.querySelector('#canva_'+id);
 var pin = palette.querySelector('#pin_'+id);
 var rect = palette.getBoundingClientRect();
 var x = event.clientX - rect.left;
 var y = event.clientY - rect.top;

 const width = palette.clientWidth;
 const height = palette.clientHeight;

 const saturation = parseFloat(x / width);
 const value = parseFloat((height - y) / height);

 const hue = palette.dataset.hue || 0;
 alpha = this.config.opacityVal;

 pin.style.left = x + 'px';
 pin.setAttribute('data-left',x);
 pin.style.top = y + 'px';
 pin.setAttribute('data-top',y);

 //set the color
 const color = this.HSVtoRGB(hue, saturation, value);

 //setting on rgba label and selected color square
 document.getElementById("demo1_"+id).innerHTML = `rgba(${color.r},${color.g},${color.b},${alpha})`;
 document.querySelector('#sele_'+id).style.backgroundColor = `rgba(${color.r},${color.g},${color.b},${alpha})`;

 //setting on temporal selected color square
 document.querySelector('#seleTemporal_'+id).style.backgroundColor = `rgba(${color.r},${color.g},${color.b},${alpha})`;

 //from rgba to hex
 var e = this.rgb2hex(`rgba(${color.r},${color.g},${color.b},${alpha})`);
 var hex = e + this.alpha2hexa(alpha);
 document.querySelector('#demo2_'+id).innerHTML = hex;
},

//move over color canvas
this.moveOnColorCanva = function(){
 var palette = document.querySelector('#canva_'+id);
 var pin = palette.querySelector('#pin_'+id);
 const x = event.clientX - palette.offsetLeft;
 const y = event.clientY - palette.offsetTop;
 const width = palette.clientWidth;
 const height = palette.clientHeight;

 const saturation = parseFloat(x / width);
 const value = parseFloat((height - y) / height);
 const hue = palette.dataset.hue || 0;
 alpha = this.config.opacityVal;

 // set the color
 const color = this.HSVtoRGB(hue, saturation, value);

 document.getElementById("demo_"+id).innerHTML = `rgba(${color.r},${color.g},${color.b},${alpha})`;
 document.querySelector('#seleTemporal_'+id).style.backgroundColor = `rgba(${color.r},${color.g},${color.b},${alpha})`;
},

Step 6: Color conversion functions

//From HSV to RGB
this.HSVtoRGB = function(hue, saturation, value){
 let r, g, b;
 const i = Math.floor(hue * 6);
 const f = hue * 6 - i;
 const p = value * (1 - saturation);
 const q = value * (1 - f * saturation);
 const t = value * (1 - (1 - f) * saturation);
 switch (i % 6) {
 case 0:
  r = value;
  g = t;
  b = p;
  break;
 case 1:
  r = q;
  g = value;
  b = p;
  break;
 case 2:
  r = p;
  g = value;
  b = t;
  break;
 case 3:
  r = p;
  g = q;
  b = value;
  break;
 case 4:
  r = t;
  g = p;
  b = value;
 break;
 case 5:
  r = value;
  g = p;
  b = q;
  break;
 }
 return {
  r: Math.round(r * 255),
  g: Math.round(g * 255),
  b: Math.round(b * 255)
 };
},

//From RGB to HEX
this.rgb2hex = function(rgb){
 rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
 return (rgb && rgb.length === 4) ? "#" +
 ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) +
 ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) +
 ("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : '';
},

//From opacity to HEX
this.alpha2hexa = function(alpha){
 var x = alpha * 255;
 var y = Math.floor(x);
 var s = y.toString(16);
 var r = y < 16 ? '0' + s : s;
 return r;
},

//Color between two color on slider
this.pickHex = function(color1, color2, weight){
 var p = weight;
 var w = p * 2 - 1;
 var w1 = (w/1+1) / 2;
 var w2 = 1 - w1;
 var rgb = [Math.round(color1[0] * w1 + color2[0] * w2),
 Math.round(color1[1] * w1 + color2[1] * w2),
 Math.round(color1[2] * w1 + color2[2] * w2)];
 return rgb;
},

Step 6.1: Define function for return selected color

//Get the selected color value
this.getColor = function(){
 if(this.config.type == 'hex'){//if hex
  return document.querySelector('#demo2_'+id).innerHTML;
 }else
 if(this.config.type == 'rgba'){//if rgba
  return document.querySelector('#demo1_'+id).innerHTML;
 }
},

Step 7: Method for show component

//build all together
this.show = function(parent){

 //override settings
 this.matchSettings();

 //template html
 var e = `
  /****** REPLACE THIS LINE WITH STEP 8... ******/
 `;

 //append inside a parent element
 if(parent == undefined){
  return e;
 }else{
  var p = document.querySelector(parent); 
  p.insertAdjacentHTML('beforeend', e);
 }

 this.drawCurrent();
 this.setPrimaryDefault();

Step 8: HTML Template (insert into step 7)

<div id='parent_`+id+`' style='width:200px'>
 <div style='background-color:white;width:200px;' class='background-colorpickerlab'>
  <div class='canva-colorpickerlab' id='canva_`+id+`' onclick="colorpicker_lab.cmp('`+id+`').selectColorCanva()" onmousemove="colorpicker_lab.cmp('`+id+`').moveOnColorCanva()">
   <div id='pin_`+id+`' class="pin-colorpickerlab active-colorpickerlab" data-left='100' data-top='5' onclick='event.stopPropagation()'></div>
  </div>

  <div class='pureColor-colorpickerlab'>
   <div id='prim1_`+id+`' onclick="colorpicker_lab.cmp('`+id+`').selectPrimary()"></div>
   <div id='prim2_`+id+`' onclick="colorpicker_lab.cmp('`+id+`').selectPrimary()"></div>
   <div id='prim3_`+id+`' onclick="colorpicker_lab.cmp('`+id+`').selectPrimary()"></div>
   <div id='prim4_`+id+`' onclick="colorpicker_lab.cmp('`+id+`').selectPrimary()"></div>
   <div id='prim5_`+id+`' onclick="colorpicker_lab.cmp('`+id+`').selectPrimary()"></div>
   <div id='prim6_`+id+`' onclick="colorpicker_lab.cmp('`+id+`').selectPrimary()"></div>
   <div id='prim7_`+id+`' onclick="colorpicker_lab.cmp('`+id+`').selectPrimary()"></div>
   <div id='prim8_`+id+`' onclick="colorpicker_lab.cmp('`+id+`').selectPrimary()"></div>
  </div>

  <div class='controls-colorpickerlab'>
   <div class="slidecontainer-colorpickerlab">
    <input type="range" min="0" max="100" value="0" class="slider-colorpickerlab" id="myRange_`+id+`" oninput="colorpicker_lab.cmp('`+id+`').selectHue()" onchange="colorpicker_lab.cmp('`+id+`').selectHue()">
   </div>
   <div class="slidecontainer-colorpickerlab">
    <input type="range" min="0" max="100" value="100" class="slider-colorpickerlab sliderOpacity-colorpickerlab" id="myRangeOpacity_`+id+`" oninput="colorpicker_lab.cmp('`+id+`').selectOpacity()" onchange="colorpicker_lab.cmp('`+id+`').selectOpacity()">
   </div>
  </div>

  <div class='selectionColor-colorpickerlab'>
   <div id='seleTemporal_`+id+`' class='seleTemporal-colorpickerlab' style='' ></div>
   <div id='sele_`+id+`' class='sele-colorpickerlab' style=''></div>
  </div>

  <div class='btnContainer-colorpickerlab'>
   <div style='display:none'>palette pure value: <span id="p1_`+id+`"></span></div>
   <div style='display:none'>opacity pure value: <span id="o1_`+id+`"></span></div>
   <div style='display:none'>temporal value : <span id="demo_`+id+`"></span></div>
   <div style='position:relative;color:#bfbdbd;'>RGBA: <span id="demo1_`+id+`"></span></div>
   <div style='position:relative;color:#bfbdbd;margin-top:5px;margin-bottom:17px;'>HEX: <span id="demo2_`+id+`"></span></div>

   <button class='btnSaveColor-colorpickerlab' onclick="colorpicker_lab.cmp('`+id+`').config.onSaveEl()">SAVE</button>
   <button class='btnSaveColor-colorpickerlab' style='border:1px solid gray' onclick="colorpicker_lab.cmp('`+id+`').config.onCancelEl()">CANCEL</button>
  </div>
 </div>
</div>

Step 9: Add some CSS style

.pureColor-colorpickerlab{
width:157px;
height:35px;
border:1px solid none;
padding:6px;
padding-top:3px;
padding-right:0px;
background-color:#2b2a2a;
}
.pureColor-colorpickerlab div{
width:30px;
height:10px;
margin-right:6px;
margin-top:5px;
background-color:white;
float:left;
cursor:pointer;
border-radius:3px;
}
.background-colorpickerlab{
background: url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>');
background-size: auto;
background-size: .5em;
border-radius: .15em;
z-index: -1;
}
.canva-colorpickerlab::before {
position: absolute;
content: "";
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>')!important;
background-size: auto;
background-size: .5em;
border-radius: .15em;
z-index: -1;
}
.canva-colorpickerlab:hover{
cursor:pointer;
}
.canva-colorpickerlab{
background: linear-gradient(to top, rgb(0, 0, 0), transparent) repeat scroll 0% 0%, rgba(0, 0, 0, 0) linear-gradient(to left, rgb(255, 0, 0), rgb(255, 255, 255)) repeat scroll 0% 0%;
width:200px;
height:130px;
position:relative;
}
.pin-colorpickerlab {
position: relative;
border: none;
border-radius: 11px;
cursor: pointer;
}
.pin-colorpickerlab.active-colorpickerlab {
width: 7px;
height: 7px;
border: 2px solid #FFF;
left:100px;
top:7px;
}
.controls-colorpickerlab{
padding:6px;
position: relative;
width: 151px;
border: 1px solid none;
background-color:#2b2a2a;
margin-top:-3px;
}
.slidecontainer-colorpickerlab {
width: 145px;
}
.slider-colorpickerlab {
-webkit-appearance: none;
width: 100%;
height: 7px;
border-radius: 5px;
background: #d3d3d3;
outline: none;
opacity: 1;
-webkit-transition: .2s;
transition: opacity .2s;
background: linear-gradient(90deg,red,#ff0,#0f0,#0ff,#00f,#f0f,red);
}
.slider-colorpickerlab::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 13px;
height: 13px;
border-radius: 50%;
background: white;
cursor: pointer;
border:none;
}
.slider-colorpickerlab::-moz-range-thumb {
width: 13px;
height: 13px;
border-radius: 50%;
background: white;
cursor: pointer;
border:none;
}
.sliderOpacity-colorpickerlab{
background: linear-gradient(90deg,transparent,#000),url('data:image/svg+xml;utf8, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><path fill="white" d="M1,0H2V1H1V0ZM0,1H1V2H0V1Z"/><path fill="gray" d="M0,0H1V1H0V0ZM1,1H2V2H1V1Z"/></svg>')!important;
background-size: auto, auto;
background-size: 100%,.25em;
}
.selectionColor-colorpickerlab{
background-color: #2b2a2a;
width: 40px;
float: right;
margin-top: -92px;
padding: 6px;
padding-right: 2px;
padding-bottom: 9px;
}
.btnContainer-colorpickerlab{
background-color:#2b2a2a;
padding:7px;
height: 90px;
padding-top: 10px;
font-family: sans-serif;
font-size: 13px;
-webkit-user-select: text!important; /* Safari */
-ms-user-select: text!important; /* IE 10+ and Edge */
user-select: text!important; /* Standard syntax */
}
.btnSaveColor-colorpickerlab{
display: inline-block;
padding: 4px 16px;
vertical-align: middle;
overflow: hidden;
text-align: center;
cursor: pointer;
white-space: nowrap;
color: #fff !important;
background-color: transparent;
border-radius: 50px;
background-color: transparent !important;
border: 1px solid #4286f4;
float:right;
margin-left:5px;
-webkit-user-select: none!important; /* Safari */
-ms-user-select: none!important; /* IE 10+ and Edge */
user-select: none!important; /* Standard syntax */
}
.sele-colorpickerlab{
background-color:rgba(255,0,0,1);
width:37px;
height:36px;
position:relative;
color:white;
margin-top: 5px;
border-radius: 5px;
}
.seleTemporal-colorpickerlab{
background-color:rgba(255,0,0,1);
width:37px;
height:36px;
position:relative;
color:white;
border-radius: 5px;
}

Step 10: Example of usage (Finally😅)

//Example
var el = colorpicker_lab.create('myColorpicker');//myColorpicker is the id
el.settings = {
 type:'rgba',//or hex by default
 onSaveEl: function(){
  var selected = colorpicker_lab.cmp('myColorpicker').getColor();
  alert('The selected color is: '+ selected);
 },
 onCancelEl: function(){
  alert("The user click on cancel button");
 }
};
el.show('body');

Check a working demo for this component:

SEE DEMO RATE THIS ARTICLE
5
35
TheRogerLAB Codes
Powered by TheRogerLAB Sandbox

info@therogerlab.com