Let’s Create a Color Picker From Scratch Native Javascript

Video Tutorial

Getting Started

We basically use Color Pickers every day whether we work as designers by creating logos and mockups or software developers when we need to choose a color theme for our new project or even simply when trying to edit your selfie picture in order to release on Instagram, so everything is related to using a Color Picker, therefore, it’s very important to understand how to make your own color picker from scratch using the native javascript with no libraries needed.

We are going to use Codepen as the online code editor for making this tutorial possible since it’s accessible by everyone and very easy to use.

Also, everyone needs to know a basic knowledge of javascript, Html & CSS alongside ES6 classes since we are going to use the class syntax to build the color picker you can take a look on the tutorial from Here.

the code editor: Codepen.io

We are also going to implement a drag and drop when making the color picker since I already covered that in a separate tutorial you can check it out Here.

Now, the first thing to consider is the HTML elements structure and the CSS style, since we are going to use the canvas to render the color picker so the main important part to focus on this tutorial is the Javascript side.

Html Project Structure:

<h2>Let's Create a Color Picker</h2>
<div class="container">
  <canvas id="color-picker"></canvas>
  <div class="info">
    <h3>Selected Color</h3> 
    <div class="selected"></div>
  </div>
</div>

And CSS Style:

html, body {
  width: 100%;
  height: 100%;
  font-family: Oxygen, sans-serif;
  text-align: center;
}

.container {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

#color-picker {
  border: 3px solid rgba(15, 15, 15, 0.2);
}

.info {
  width: 12em;
  display: flex;
  margin-left: 4em;
  flex-direction: row;
  justify-content: space-between;
}

.selected {
  width: 50px;
  height: 50px;
  border-radius: 100%;
  border: 2px solid rgba(15, 15, 15, 0.2);
}

For the CSS Styling we are basically using the flexbox to align the color picker canvas and the info div in the center of the webpage and give the canvas a border so when rendering the picker we can easily see the borders of where the colors end.

Make sure to choose Babel as the compiler option on Codepen javascript.

Draw the Color Picker

For the rendering process of the color gradient in order to finally form the color picker we need to use the WebGL Canvas API for drawing 2d colors on the canvas, likely we can use the 2D context API for easily rendering color gradient and form the color picker palette.

First, let’s create the Picker Class that is going to be responsible for drawing and listening for events.

class Picker {
  constructor(target, width, height) {
    this.target = target;
    this.width = width;
    this.height = height;
    this.target.width = width;
    this.target.height = height;
    //Get context 
    this.context = this.target.getContext("2d");
    //Circle (Color Selector Circle)
    this.pickerCircle = { x: 10, y: 10, width: 7, height: 7 };

    draw() {
      //Drawing Here
    }
}

For the colorPicker, we need there parameters the canvas target element, the canvas width, and its height in order to be able to create and render on the canvas, we simply store those values on the Picker class as local variables on the constructor, get the rendering context from canvas passing it 2d parameter and create pickerCircle object it’s position and dimension (x, y, width, height) where the circle only need a radius instead of width and height but we are going to use it that way to make it easier for us later to work with.

Now create a build method which is going to take care of rendering the color gradient of the ColorPicker palate and the picker circle (color selector).

...
build() {
//Create a Gradient Color (colors change on the width)
let gradient = this.context.createLinearGradient(0, 0, this.width, 0);
//Add Color Stops to the Gradient (from 0 to 1)
gradient.addColorStop(0, "rgb(255, 0, 0)");
gradient.addColorStop(0.15, "rgb(255, 0, 255)");
gradient.addColorStop(0.33, "rgb(0, 0, 255)");
gradient.addColorStop(0.49, "rgb(0, 255, 255)");
gradient.addColorStop(0.67, "rgb(0, 255, 0)");
gradient.addColorStop(0.84, "rgb(255, 255, 0)");
gradient.addColorStop(1, "rgb(255, 0, 0)");
//Add color picker colors (red, green, blue, yellow...)
//Render the Color Gradient from the 0's position to the full width and height
this.context.fillStyle = gradient; ///, set it's style to be the color gradient
this.context.fillRect(0, 0, this.width, this.height); ///< render it
}
...

We create a gradient on the width (which means the colors will change on the width dimension), then we add different colors to it because a gradient takes a value from 0 to 1, lastly we set the fill style to the created gradient and we render it to the canvas starting from 0x, 0y and full width & height.

The above gradients aren’t enough because it is missing the White and Black colors on the Color palate so in this case, we have to add a new gradient on the height representing the white and black colors.

...
build() {
...
//Apply black and white (on the height dimension instead of the width)
gradient = this.context.createLinearGradient(0, 0, 0, this.height);
//We have two colors so 0, 0.5 and 1 needs to be used.
gradient.addColorStop(0, "rgba(255, 255, 255, 1)");
gradient.addColorStop(0.5, "rgba(255, 255, 255, 0)");
gradient.addColorStop(0.5, "rgba(0, 0, 0, 0)");
gradient.addColorStop(1, "rgba(0, 0, 0, 1)");
//set style and render it.
this.context.fillStyle = gradient;
this.context.fillRect(0, 0, this.width, this.height);
...

We also have to render the picker circle selector as a small circle on the color picker make sure to render it last so it will go above the color gradient since the canvas renders from the bottom to the top (first to last).

...
build() { 
...
//Circle 
this.context.beginPath();
//Arc renders a circle depending on the position, radius and arc
this.context.arc(this.pickerCircle.x, this.pickerCircle.y, this.pickerCircle.width, 0, Math.PI * 2);
//Render it in black but not fill (only stroke)
this.context.strokeStyle = "black";
//Render the circle stroke and close the rendering path
this.context.stroke();
this.context.closePath();
}

The circle needs three main parameters the x, y position of the circle and the radius which in this case we use the width we did register earlier and the arc we start from 0 till PI * 2 which means a full circle, if you know the basics of Math this should be very easy for you.

Let’s render it, call the build method on the draw and create an instance of the picker class.

//Create an instance passing it the canvas, width and height
let picker = new Picker(document.getElementById("color-picker"), 250, 220);

//Draw 
picker.draw();

Take a look on the preview tab you should see the color picker palate get rendered for you will all the possible color values with the picker circle sitting on the top left corner, now we need to add events listeners to listen for drag and drop of the circle and then click to select a color.

Color Selector

So for selecting colors, we are going to add two different way for the color picker, either drag and drop the picker circle on a specific color portion or click on the color palate and circle should change it’s position to the target position.

Let’s add a new method responsible for listening for events we are going to have three main events (onMouseDown, onMouseMove and onMouseUp).

...
listenForEvents() {
    let isMouseDown = false;
    const onMouseDown = (e) => {
      let currentX = e.clientX - this.target.offsetLeft;
      let currentY = e.clientY - this.target.offsetTop;
      if(currentY > this.pickerCircle.y && currentY < this.pickerCircle.y + this.pickerCircle.width && currentX > this.pickerCircle.x && currentX < this.pickerCircle.x + this.pickerCircle.width) {
        isMouseDown = true;
      } else {
        this.pickerCircle.x = currentX;
        this.pickerCircle.y = currentY;
      }
    }
    const onMouseMove = (e) => {
      if(isMouseDown) {
       let currentX = e.clientX - this.target.offsetLeft;
       let currentY = e.clientY - this.target.offsetTop;
        this.pickerCircle.x = currentX;
        this.pickerCircle.y = currentY;
      }
    }
    const onMouseUp = () => {
      isMouseDown = false;
    }
    //Register 
    this.target.addEventListener("mousedown", onMouseDown);
    this.target.addEventListener("mousemove", onMouseMove);
    //Mouse up on the Document     
    document.addEventListener("mouseup", onMouseUp);
  }

OnMouseDown listener we first get the current x and y mouse position relative to the canvas by subtracting the offset of the canvas element then we check the mouse is clicking on the picker circle using its x, y position and it’s radius(width or height), the checking statement is pretty straightforward else if the mouse is clicking somewhere else rather than the circle so just change the picker circle position to the clicked position.

OnMouseMove we check if the mouse is down on the right element using the isMouseDown variable that we triggered on the onMouseDown listener then we simply make the circle position follow the mouse relative position.

OnMouseUp which is being registered on the document as an event listener for the mouse Up event unlike the others which are registered on the canvas (target element), we set isMouseDown to false since it’s no longer down.

Now we only need to register the events on the constructor so it gets registered and bound to the right elements whenever we create the picker instance.

...
constructor() {
...
this.listenForEvents();
...
}
...

Now try to click on the canvas or drag the picker circle around it should follow you and get where ever you tell it to, pretty cool!

The picker circle is moving but there is no proper use of it, therefore let’s add the color preview to match up the currently selected color.

Picked Color

Forgetting the picked color we need to tell the canvas context to give us the pixel data pointed by the circle on the picker palate, let’s add a method for that on the Picker class.

...
getPickedColor() {
    //Get the Image Data (pixel value) pointed by the circle by using it's current position
    //getImageData returns an object that has the pixel data (1, 1) is for getting only one pixel.
    let imageData = this.context.getImageData(this.pickerCircle.x, this.pickerCircle.y, 1, 1);
    //Return back an object has the RGB color value of the pointed pixel.
    //The data is an array holds the red, green, blue and alpha values of the current pixel 
    return { r: imageData.data[0], g: imageData.data[1], b: imageData.data[2] };
}
...

the Above method is responsible for giving the RGB color value pointed by the circle on the canvas using the context getImageData method (you can as many pixels data as you like but we only need to get one-pixel RGB value).

When the circle picker moves (changes its position) only the picker class knows about it but we need to export it as an event so we can run a callback (specific function of our own) whenever the circle change its position passing it the current picked color, so we have to use a callback event.

onChange(callback) {
  //Save Callback function reference on the class
  this.onChangeCallback = callback;
}
//And add it as an event listener on the mouseMove (when ever it moves the callback gets executed)
listenForEvents() {
...
  //Mouse move event on the canvas, call callback passing it the current color 
  this.target.addEventListener("mousemove", () => this.onChangeCallback(this.getPickedColor()));
...

Now we can call the onChange method passing it a callback which is going to receive the picked color whenever the circle position changes.

Let’s listen for onChange and change the preview target background color to the currently selected color.

//On Circle position change 
picker.onChange((color) => {
  //Get the preview DOM element
  let selected = document.getElementsByClassName("selected")[0];
  //Change it's backagroundColor to the current color (rgb CSS function)
  selected.style.backgroundColor = `rgb(${color.r}, ${color.g}, ${color.b})`;
});
//NOTE: Remeber we are return a color object that has a three properties(Red, Green and Blue)

The Final Test, you should be able now to use the color picker as you wish by dragging/dropping and selecting a specific color from the Canvas and you will get the color showed to you on the Preview circle.

The Final Color Picker: CodePen Embed - Color Picker class Picker { constructor(target, width, height) { this.target = target; this.width = width; this.height = height…codepen.io

What’s Next

From here as you know the basics of how color pickers actually works you can create a different color palates combinations plus add more features to the picker such as color value input in Hex or HSL or other available color formats.

Feel free to share your examples with us.

No Comments Yet