Skip to content
Advertisement

How to create a color picker without a canvas? [closed]

I would like to create a custom color picker without a canvas or JavaScript library. I have found a few websites that use a canvas, but is it possible to do it with just a background image? Here is my code:

#rainbow{
  height: 15px;
  background-color: red;
  background-image: linear-gradient(to right, Red , Orange, Yellow, Green, Blue, Indigo, Violet);
  border-radius: 3px;
  border: 1px solid black;
  margin: 5px;
}
#saturation{
height:15px;
background-color:red;
background-image: linear-gradient(to left, red, gray);
border-radius: 3px;
border: 1px solid black;
margin: 5px;
}
#grayscale{
height:15px;
background-color:gray;
background-image: linear-gradient(to right, black, white);
margin: 5px;
border-radius: 3px;
border:1px solid black;
}
#color-picker{
background-color:whitesmoke;
box-shadow:lightgray 2px 2px;
padding: 10px;
border-radius: 7px;
<!DOCTYPE html>
<html>
<body>
<div id="color-picker">
<div id="rainbow"></div>
<div id="grayscale"></div>
<div id="saturation"></div>
<label for="hex">HEX:</label>
<input id="hex" type="text" value="#ffffff"/>
<label for="rgb">RGB:</label>
<input id="rgb" type="text" value="255,255,255"/>
<label for="hsl">HSL:</label>
<input id="hsl" type="text" value="0,0,100"/>
</div>
</body>
</html>

Advertisement

Answer

The question uses linear gradients to fill the color picker chart. This gives colors in the x direction (but not the y) and as the gradient is linear we can work out the color which the mouse is over by doing a linear interpolation between the color that starts the section it is in (e.g. red) and the color that ends the section it is in (orange) depending on how far along the section the mouse is.

The components we are going to use are:

  listening for a mouse move event

  event.clientX - this gives the x offset of the mouse within an element

  a formula for calculating the rgb values (see code in the snippet)

  conversions from rgb to hex and hsl so the relevant input elements can be updated

cursor as a semi transparent svg circle

Note we are working in rgb because of the choice of a linear 1 dimensional color chart and then converting to hsl and hex so all 3 types of color can be shown.

let rgbRainbow= [[255,0,0], [255, 255, 0], [255, 165, 0], [0, 128, 0], [0, 0, 255], [75, 0, 130], [238, 130, 238]];     
    let rainbow = document.getElementById('rainbow');
    let rainbowWidth = rainbow.offsetWidth;
    let sectionWidth = rainbowWidth /(rgbRainbow.length - 1);
    
// adapted from https://stackoverflow.com/questions/22218140/calculate-the-color-at-a-given-point-on-a-gradient-between-two-colors
function colorAt(x) {
    let section = Math.floor(x / sectionWidth);
    let pc = (x%sectionWidth)/sectionWidth;
    
    let c1 = rgbRainbow[section];
    let c2 = (section >= (rgbRainbow.length-1)) ? c1 : rgbRainbow[section + 1]; // there appears to be an end affect
    
    return [Math.floor(c1[0] + pc*(c2[0]-c1[0])), Math.floor(c1[1] + pc*(c2[1]-c1[1])), Math.floor(c1[2] + pc*(c2[2]-c1[2]))];
}

// adapted from https://gmigdos.wordpress.com/2011/01/13/javascript-convert-rgb-values-to-hsl/
function rgb2hsl(rgbArr){ //returns a string of this form 'H,S,L
    var r1 = rgbArr[0] / 255;
    var g1 = rgbArr[1] / 255;
    var b1 = rgbArr[2] / 255;
 
    var maxColor = Math.max(r1,g1,b1);
    var minColor = Math.min(r1,g1,b1);

    var L = (maxColor + minColor) / 2 ;
    var S = 0;
    var H = 0;
    if(maxColor != minColor){
        if(L < 0.5){
            S = (maxColor - minColor) / (maxColor + minColor);
        }else{
            S = (maxColor - minColor) / (2.0 - maxColor - minColor);
        }
        if(r1 == maxColor){
            H = (g1-b1) / (maxColor - minColor);
        }else if(g1 == maxColor){
            H = 2.0 + (b1 - r1) / (maxColor - minColor);
        }else{
            H = 4.0 + (r1 - g1) / (maxColor - minColor);
        }
    }
 
    L = Math.floor(L * 100);
    S = Math.floor(S * 100);
    H = Math.floor(H * 60);
    if(H<0){
        H += 360;
    }
    return H + ',' + S + ',' + L;
}
function rgb2hex(rgbArr) {
  function intToHex(i) {
    let hex = i.toString(16);
    return hex.length == 1 ? "0" + hex : hex;
}
  return "#" + intToHex(rgbArr[0]) + intToHex(rgbArr[1]) + intToHex(rgbArr[2]);
}

function mouseMovedTo(x) {
  let c = colorAt(x);
  document.getElementById('color').style.backgroundColor = 'rgb('+c[0]+','+c[1]+','+c[2]+')';
  document.getElementById('rgb').value = c;
  document.getElementById('hsl').value = rgb2hsl(c);
  document.getElementById('hex').value = rgb2hex(c);
}
#rainbow{
      height: 150px;
      background-color: red;
      background-image: linear-gradient(to right, Red , Orange, Yellow, Green, Blue, Indigo, Violet);
      border-radius: 3px;
      border: 1px solid black;
      cursor: url("data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='16px' height='16px' xml:space='preserve'%3E %3Ccircle cx='8' cy='8' r='8' fill='rgba(0,0,0,0.2)' /%3E %3C/svg%3E") 8 8, auto;
    }
    #grayscale{
    height:15px;
    background-color:gray;
    background-image: linear-gradient(to right, black, white);
    margin-top: 5px;
    border-radius: 3px;
    border:1px solid black;
    }
    #color-picker{
    background-color:whitesmoke;
    box-shadow:lightgray 2px 2px;
    padding: 10px;
    border-radius: 7px;
    }
<div id="color-picker" >
    <div id="rainbow" onmousemove="mouseMovedTo(event.clientX);"></div>
    <div id="grayscale"></div>
    <label for="hex">HEX:</label>
    <input id="hex" type="text" value="#ffffff"/>
    <label for="rgb">RGB:</label>
    <input id="rgb" type="text" value="255,255,255"/>
    <label for="hsl">HSL:</label>
    <input id="hsl" type="text" value="0,0,100"/>
    
    <div id="color" style="width:100px; height: 2em; border: 1px solid black;">Current color</div>
    </div>
User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement