Let's create a Color Picker from scratch with HTML5 Canvas, Javascript and CSS3

by Janeth Kent Date: 01-06-2023 javascript html5 canvas css3

HTML5 Canvas is a technology that allows developers to generate real-time graphics and animations using JavaScript. It provides a blank canvas on which graphical elements, such as lines, shapes, images and text, can be drawn and manipulated with great flexibility and control.

Here are some key concepts about HTML5 Canvas:

1. Canvas element: The <canvas> element is the base of the canvas on which the graphics are drawn. It is defined by HTML tags and can be sized using the `width` and `height` attributes. All graphic elements are drawn within this canvas.

2. Context: The context (`context`) is the object that provides methods and properties for drawing on the canvas. There are two types of context: 2D and WebGL. For 2D graphics, the 2D context (`context2d`) is used, which is more common. To access the 2D context, you use the getContext('2d') method on the <canvas> element.

3. Coordinates and coordinate system: The Canvas canvas uses a coordinate system in which `(0, 0)` represents the upper left corner of the canvas and positive coordinates increase downwards and to the right. This means that the highest values of `x` are to the right and the highest values of `y` are to the bottom.

4. Drawing methods: The 2D context provides a wide range of methods for drawing different graphical elements on the canvas, such as lines, rectangles, circles, curves, images and text. Some of the most common methods include fillRect(), strokeRect(), arc(), drawImage() y fillText().

5. Styles and attributes: The 2D context also allows you to set styles and attributes for graphic elements. You can set stroke and fill colours, line thickness, typography and other attributes that affect the visual appearance of graphics.

6. Animations: One of the advantages of HTML5 Canvas is its ability to create fluid and dynamic animations. This can be achieved by using techniques such as periodic updating of the canvas, the use of the requestAnimationFrame() and the manipulation of the graphic elements in each frame.

HTML5 Canvas offers a wide range of creative possibilities and is used in many areas, such as online games, data visualisations, interactive applications and generative graphics. It is a powerful tool for web development and gives developers complete control over the graphical representation in the browser.

In this tutorial we are going to explain how to use the Canvas element to create a simple colour picker.

We start with the basic HTML code of the page:

 
<!DOCTYPE html>
 <html lang="en">
 <head>
 <meta charset="utf-8" />
 <title>Colorpicker demo</title>
 </head>
 <body>
 
 

We go on to define some CSS styles for the elements on the page. Styles are set for the body, a header (h2) and a Google Fonts font called "Open Sans" is imported.

 
<style>
 @import url(https://fonts.googleapis.com/css?family=Open+Sans);
 body {
 margin: 0;
 padding: 0;
 background-color: #e6e6e6;
 }
 h2 {
 background-color: #dbdbdb;
 margin: 0;
 margin-bottom: 15px;
 padding: 10px;
 font-family: 'Open Sans';
 }
 /* Additional CSS for page elements */
 
 

We continue with our heading indicating the purpose of the colour picker.

 
<h2>Canvas Color Picker</h2>
 

Then we create the two elements that will display the selected colour in RGBA and HEX format: the identifiers txtRgba and txtHex will be used to update the values later from the JavaScript code.

<label for="color-input" id="color-label" style="background-color: red"></label>
<input type="checkbox" id="color-input" checked></input>
 

Aquí creamos una etiqueta <label> con el identificador color-label. Esta etiqueta se utiliza como muestra visual del color seleccionado. También hay un <input> de tipo checkbox con el identificador color-input, que se utiliza para controlar la visibilidad del selector de color.

Next we create a <div> container with the colour-picker identifier, which contains two <canvas> elements. The first <canvas> with the colour-block identifier is used as the main canvas where the colour is selected. The second <canvas> with the colour-strip identifier is used to display a colour strip to select the saturation component of the colour.

 
<div id="color-picker">
<canvas id="color-block" height="150" width="150"></canvas>
<canvas id="color-strip" height="150" width="30"></canvas>
</div>
 

Now the fun begins...
Let's see how our JavaScript works:

 
<script type="text/javascript">
 // This is where the JavaScript code block begins
 
 var colorBlock = document.getElementById('color-block');
var ctx1 = colorBlock.getContext('2d');
var width1 = colorBlock.width;
var height1 = colorBlock.height;
 

These lines of code get the main canvas element with the identifier "colour-block" from the HTML document. Then, the 2d context of the canvas is fetched using getContext('2d'). We also store the dimensions (width and height) of the canvas in the variables width1 and height1. We continue:

 
var colorStrip = document.getElementById('color-strip');
var ctx2 = colorStrip.getContext('2d');
var width2 = colorStrip.width;
var height2 = colorStrip.height;
   
 

As you can see, the code is similar to the previous one, but in this case they obtain the canvas element of the colour strip with the identifier "colour-strip". The 2d context of the canvas is obtained and the dimensions are stored in the variables width2 and height2.

Now we have to get the HTML document elements with the identifiers "colour-label", "txtRgba" and "txtHex" and we have to store them in corresponding variables. These elements are used to display and update the selected colour values.

 
var colorLabel = document.getElementById('color-label');
var txtRgba = document.getElementById('txtRgba');
var txtHex = document.getElementById('txtHex');
 
 

Let's add the variables needed to track the position of the mouse on the canvas to control whether it is dragging or not: x and y store the mouse coordinates, drag indicates whether the mouse is dragging and rgbaColor stores the initial value of the colour in RGBA format (red, green, blue and transparency).

 
var x = 0;
var y = 0;
var drag = false;
var rgbaColor = 'rgba(255,0,0,1)';
 
 

And now we define the colour gradients on the canvases. In the canvas colourBlock, we draw a rectangle that covers the whole canvas and then call the fill function

 
ctx1.rect(0, 0, width1, height1);
  fillGradient();
    
    ctx2.rect(0, 0, width2, height2);
    var grd1 = ctx2.createLinearGradient(0, 0, 0, height1);
    grd1.addColorStop(0, 'rgba(255, 0, 0, 1)');
    grd1.addColorStop(0.17, 'rgba(255, 255, 0, 1)');
    grd1.addColorStop(0.34, 'rgba(0, 255, 0, 1)');
    grd1.addColorStop(0.51, 'rgba(0, 255, 255, 1)');
    grd1.addColorStop(0.68, 'rgba(0, 0, 255, 1)');
    grd1.addColorStop(0.85, 'rgba(255, 0, 255, 1)');
    grd1.addColorStop(1, 'rgba(255, 0, 0, 1)');
    ctx2.fillStyle = grd1;
    ctx2.fill();
  
 

We add the function that is executed when you click on the colour strip canvas (colourStrip) with the following characteristics: when you click, you have to get the coordinates (offsetX and offsetY) of the point where you clicked. Then, the pixel colour corresponding to those coordinates is obtained using getImageData(). The result is stored in imageData, which is an object containing information about the RGBA components of the pixel. An rgbaColor string is constructed using these values and the fillGradient() function is called to update the colour in the main canvas.

 
function click(e) {
 x = e.offsetX;
 y = e.offsetY;
 var imageData = ctx2.getImageData(x, y, 1, 1).data;
 rgbaColor = 'rgba(' + imageData[0] + ',' + imageData[1] + ',' + imageData[2] + ',1)';
 fillGradient();
 }
 

We create the function fillGradient() which draws the gradients on the main canvas (colourBlock) to represent the selected colour. First, the fill colour is set to ctx1 with the value of rgbaColor and a rectangle is drawn covering the whole canvas. Then, two linear gradients, grdWhite and grdBlack, are created using the canvas context of the colour strip (ctx2). These gradients are used to create a gradient effect on the main canvas, providing areas of black and white to adjust the brightness and contrast of the selected colour.

 
function fillGradient() {
   ctx1.fillStyle = rgbaColor;
   ctx1.fillRect(0, 0, width1, height1);

   var grdWhite = ctx2.createLinearGradient(0, 0, width1, 0);
   grdWhite.addColorStop(0, 'rgba(255,255,255,1)');
   grdWhite.addColorStop(1, 'rgba(255,255,255,0)');
   ctx1.fillStyle = grdWhite;
   ctx1.fillRect(0, 0, width1, height1);
   
   var grdBlack = ctx2.createLinearGradient(0, 0, 0, height1);
   grdBlack.addColorStop(0, 'rgba(0,0,0,0)');
   grdBlack.addColorStop(1, 'rgba(0,0,0,1)');
   ctx1.fillStyle = grdBlack;
   ctx1.fillRect(0, 0, width1, height1);
   }
 
 

The following functions are used to control the user's interaction with the main canvas (colourBlock). When the user presses the mouse button inside the canvas (mousedown), drag is set to true to indicate dragging. The changeColor() function is called to update the selected colour.

During mousemove, if drag is true, changeColor() is called to update the selected colour while dragging the mouse.

When the mouse button is released inside the canvas (mouseup), drag is set to false to indicate that dragging is finished.

 
function mousedown(e) {
 drag = true;
 changeColor(e);
 }
 
 function mousemove(e) {
   if (drag) {
   changeColor(e);
   }
   }
   
   function mouseup(e) {
   drag = false;
   }
 
 

Let's go ahead with the code for the changeColor() function used to update the selected colour when the user interacts with the main canvas. First, we get the coordinates of the point where the interaction occurred (offsetX and offsetY). Then, the corresponding pixel colour is obtained using getImageData() and the rgbaColor variable is updated.

After that, the background colour of the colourLabel element is updated with the selected colour, the colour value is displayed in RGBA format in the txtRgba element and the colour is converted from RGBA to hexadecimal format using the rgbaToHex() function. The result is displayed in the txtHex element and is also printed to the console.

 
function changeColor(e) {
 x = e.offsetX;
 y = e.offsetY;
 var imageData = ctx1.getImageData(x, y, 1, 1).data;
 rgbaColor = 'rgba(' + imageData[0] + ',' + imageData[1] + ',' + imageData[2] + ',1)';
 colorLabel.style.backgroundColor = rgbaColor;
 txtRgba.innerHTML = rgbaColor;

 var hexColor = rgbaToHex(rgbaColor);
   console.log(hexColor);
   txtHex.innerHTML = hexColor;
   }
 
 

These next lines of code assign the event handlers to the canvas elements and the colour strip element. When the colour strip is clicked, the click() function is executed. When the mouse is pressed, released or moved within the main canvas, the corresponding functions (mousedown(), mouseup(), mousemove()) are executed to control the interaction and update the selected colour.

colorStrip.addEventListener("click", click, false);
colorBlock.addEventListener("mousedown", mousedown, false);
colorBlock.addEventListener("mouseup", mouseup, false);
colorBlock.addEventListener("mousemove", mousemove, false);
 

The rgbaToHex() function converts a colour in RGBA format to hexadecimal format. First, the R, G, B and A component values of the RGBA colour are extracted using regular expressions. Then, the R, G and B values are converted to hexadecimal format using toString(16) and padStart(2, '0') to make sure they have two digits. Finally, the hexadecimal values are combined and the colour is returned in hexadecimal format.

 
function rgbaToHex(rgbaColor) {
 var values = rgbaColor.match(/d+/g);
 var r = parseInt(values[0]);
 var g = parseInt(values[1]);
 var b = parseInt(values[2]);
 var a = parseFloat(values[3]);

 var hexR = r.toString(16).padStart(2, '0');
 var hexG = g.toString(16).padStart(2, '0');
 var hexB = b.toString(16).padStart(2, '0');
 var hexColor = '#' + hexR + hexG + hexB;
 return hexColor;
 }
 
 

Here is all the code:

 
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Colorpicker demo</title>
</head>
<body>
<style>
   @import url(https://fonts.googleapis.com/css?family=Open+Sans);
   body {
   margin: 0;
   padding: 0;
   background-color: #e6e6e6;
   }
   h2 {
   background-color: #dbdbdb;
   margin: 0;
   margin-bottom: 15px;
   padding: 10px;
   font-family: 'Open Sans';
   }
   #color-input {
   display: none;
   }
   #color-label {
   margin-left: 15px;
   position: absolute;
   height: 30px;
   width: 50px;
   } 
   #color-input:checked ~ #color-picker {
   opacity: 1;
   }
   #color-picker {
   position: absolute;
   left: 70px; 
   background-color: white;
   height: 150px;
   width: 185px;
   border: solid 1px #ccc;
   opacity: 0;
   padding: 5px;
   }
   canvas:hover {
   cursor: crosshair;
   }
   </style>
<h2>Canvas Color Picker</h2>
<div><p>Color in RGBA is: <span id="txtRgba"></span> </p>
<div><p>Color in HEX is: <span id="txtHex"></span> </p>
</div>
<label for="color-input" id="color-label" style="background-color: red"></label>
<input type="checkbox" id="color-input" checked></input>
<div id="color-picker">
<canvas id="color-block" height="150" width="150"></canvas>
<canvas id="color-strip" height="150" width="30"></canvas>
</div>
<script type="text/javascript">
   var colorBlock = document.getElementById('color-block');
   var ctx1 = colorBlock.getContext('2d');
   var width1 = colorBlock.width;
   var height1 = colorBlock.height;
var colorStrip = document.getElementById('color-strip');
   var ctx2 = colorStrip.getContext('2d');
   var width2 = colorStrip.width;
   var height2 = colorStrip.height;
var colorLabel = document.getElementById('color-label');
   var txtRgba = document.getElementById('txtRgba');
   var txtHex = document.getElementById('txtHex');
 
var x = 0;
   var y = 0;
   var drag = false;
   var rgbaColor = 'rgba(255,0,0,1)';
ctx1.rect(0, 0, width1, height1);
   fillGradient();
ctx2.rect(0, 0, width2, height2);
   var grd1 = ctx2.createLinearGradient(0, 0, 0, height1);
   grd1.addColorStop(0, 'rgba(255, 0, 0, 1)');
   grd1.addColorStop(0.17, 'rgba(255, 255, 0, 1)');
   grd1.addColorStop(0.34, 'rgba(0, 255, 0, 1)');
   grd1.addColorStop(0.51, 'rgba(0, 255, 255, 1)');
   grd1.addColorStop(0.68, 'rgba(0, 0, 255, 1)');
   grd1.addColorStop(0.85, 'rgba(255, 0, 255, 1)');
   grd1.addColorStop(1, 'rgba(255, 0, 0, 1)');
   ctx2.fillStyle = grd1;
   ctx2.fill();
function click(e) {
   x = e.offsetX;
   y = e.offsetY;
   var imageData = ctx2.getImageData(x, y, 1, 1).data;
   rgbaColor = 'rgba(' + imageData[0] + ',' + imageData[1] + ',' + imageData[2] + ',1)';
   fillGradient();
   
   }
function fillGradient() {
   ctx1.fillStyle = rgbaColor;
   ctx1.fillRect(0, 0, width1, height1);
 var grdWhite = ctx2.createLinearGradient(0, 0, width1, 0);
   grdWhite.addColorStop(0, 'rgba(255,255,255,1)');
   grdWhite.addColorStop(1, 'rgba(255,255,255,0)');
   ctx1.fillStyle = grdWhite;
   ctx1.fillRect(0, 0, width1, height1);
 var grdBlack = ctx2.createLinearGradient(0, 0, 0, height1);
   grdBlack.addColorStop(0, 'rgba(0,0,0,0)');
   grdBlack.addColorStop(1, 'rgba(0,0,0,1)');
   ctx1.fillStyle = grdBlack;
   ctx1.fillRect(0, 0, width1, height1);
   }
function mousedown(e) {
   drag = true;
   changeColor(e);
   }
function mousemove(e) {
   if (drag) {
   changeColor(e);
   }
   }
function mouseup(e) {
   drag = false;
   }
function changeColor(e) {
   x = e.offsetX;
   y = e.offsetY;
   var imageData = ctx1.getImageData(x, y, 1, 1).data;
   rgbaColor = 'rgba(' + imageData[0] + ',' + imageData[1] + ',' + imageData[2] + ',1)';
   colorLabel.style.backgroundColor = rgbaColor;
   txtRgba.innerHTML = rgbaColor;
 
 var hexColor = rgbaToHex(rgbaColor);
   console.log(hexColor);
   txtHex.innerHTML = hexColor;
   }
colorStrip.addEventListener("click", click, false);
colorBlock.addEventListener("mousedown", mousedown, false);
   colorBlock.addEventListener("mouseup", mouseup, false);
   colorBlock.addEventListener("mousemove", mousemove, false);
function rgbaToHex(rgbaColor) {
   var values = rgbaColor.match(/d+/g);
   var r = parseInt(values[0]);
   var g = parseInt(values[1]);
   var b = parseInt(values[2]);
   var a = parseFloat(values[3]);
   var hexR = r.toString(16).padStart(2, '0');
   var hexG = g.toString(16).padStart(2, '0');
   var hexB = b.toString(16).padStart(2, '0');
   var hexColor = '#' + hexR + hexG + hexB;
   return hexColor;
   }
 
</script>
</body>
</html>
 

I hope that this tutorial has demonstrated the great potential that exists in developing applications using Canvas. There are much more advanced applications, and even games are being developed using this technology. It is therefore a field worth exploring, as it offers the possibility to create amazing and surprising things.

 
by Janeth Kent Date: 01-06-2023 javascript html5 canvas css3 hits : 2710  
 
Janeth Kent

Janeth Kent

Licenciada en Bellas Artes y programadora por pasión. Cuando tengo un rato retoco fotos, edito vídeos y diseño cosas. El resto del tiempo escribo en MA-NO WEB DESIGN AND DEVELOPMENT.

 
 
 

Related Posts

How to upload files to the server using JavaScript

In this tutorial we are going to see how you can upload files to a server using Node.js using JavaScript, which is very common. For example, you might want to…

How to combine multiple objects in JavaScript

In JavaScript you can merge multiple objects in a variety of ways. The most commonly used methods are the spread operator ... and the Object.assign() function.   How to copy objects with…

The Payment Request API: Revolutionizing Online Payments (Part 2)

In the first part of this series, we explored the fundamentals of the Payment Request API and how it simplifies the payment experience. Now, let's delve deeper into advanced features…

The Payment Request API: Revolutionizing Online Payments (Part 1)

The Payment Request API has emerged as the new standard for online payments, transforming the way transactions are conducted on the internet. In this two-part series, we will delve into…

How do you stop JavaScript execution for a while: sleep()

A sleep()function is a function that allows you to stop the execution of code for a certain amount of time. Using a function similar to this can be interesting for…

Mastering array sorting in JavaScript: a guide to the sort() function

In this article, I will explain the usage and potential of the sort() function in JavaScript.   What does the sort() function do?   The sort() function allows you to sort the elements of…

Infinite scrolling with native JavaScript using the Fetch API

I have long wanted to talk about how infinite scroll functionality can be implemented in a list of items that might be on any Web page. Infinite scroll is a technique…

Sorting elements with SortableJS and storing them in localStorage

SortableJS is a JavaScript extension that you will be able to use in your developments to offer your users the possibility to drag and drop elements in order to change…

What is a JWT token and how does it work?

JWT tokens are a standard used to create application access tokens, enabling user authentication in web applications. Specifically, it follows the RFC 7519 standard. What is a JWT token A JWT token…

Template Literals in JavaScript

Template literals, also known as template literals, appeared in JavaScript in its ES6 version, providing a new method of declaring strings using inverted quotes, offering several new and improved possibilities. About…

How to use the endsWith method in JavaScript

In this short tutorial, we are going to see what the endsWith method, introduced in JavaScript ES6, is and how it is used with strings in JavaScript. The endsWith method is…

Loading images by resolution with HTML5

Normally the way to load images in HTML is through the img element to which we pass as a parameter the URL of the image to load. But since HTML5…

Clicky