Create a Color Picker With JavaScript

Create a Color Picker With JavaScript

The RGB color mode is a color model formed by combining three primary colors: red, green, and blue. It can be used to describe the colors we see in a digital way, where the intensity of each color is represented by a number between 0 and 255. Therefore, we can know the value of each color in RGB.

Click Here to Color Picker



Obtaining and processing color data

Step1. Raw Data

color-list
We can obtain some color data and their English names from RGB to Color Name Mapping (Triplet and Hex). Next, let’s process it. Run the following lines of code in the console:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
copy(
Array.prototype.filter
.call(document.querySelectorAll("tr"), (item) => {
return (
item?.children?.length === 6 && item?.children?.[0].tagName !== "TH"
);
})
.map((item) => {
return {
name: item.children[0].innerText,
rgb: item.children[2].innerText,
};
})
);

Then we can get an array of color data called colorList, which has been copied to the system clipboard by the program above. The result of this array is as follows:

1
2
3
4
5
6
7
8
9
10
11
[
{
"name": "Grey",
"rgb": "84;84;84"
},
{
"name": "Grey, Silver",
"rgb": "192;192;192"
},
...
]

Step2. Deduplication

Next, we use the uniqBy method from lodash to remove duplicates from our color array.
copy(_.uniqBy(colorList, 'rgb'))

Step3. Formatting

Separate the RGB values in the color data into individual properties for easy searching and calculation in subsequent steps.

1
2
3
4
5
6
7
8
9
10
11
copy(
colorList.map((item) => {
const [r, g, b] = item.rgb.split(";");
return {
name: item.name,
r,
g,
b,
};
})
);

Core function: Obtain the color name based on an RGB(A) value.

color-mapping
The basic principle of using the RGB coordinate system to describe colors is already very clear. This method allows us to describe any color in three-dimensional space, where the XYZ coordinates of a color are its RGB values. Through this approach, we can intuitively understand the position of each color in space and their relative positions with respect to one another.

Once we have all the coordinates for each color, we can use basic mathematical algorithms to calculate the distance between any two colors in space. Based on our definition of distance, we can obtain a distance from a target color to every known color and then sort them by size in order to find the closest match.

The core of this algorithm lies in calculating the distance between two colors. We can use a simple formula: The distance between two points equals the square root of the sum of squares of their differences along each dimension. That is, for two points (x1,y1) and (x2,y2) on a 2D plane, their distance d can be expressed as:

d = √[(x1-x2)^2 + (y1-y2)^2]

Similarly, we can express distances between points on a 3D plane as:

d = √[(x1-x2)^2 + (y1-y2)^2 + (z1-z2)^2]

Here, (x1,y1,z1) and (x2,y2,z22) represent coordinates for two colors within an RGB coordinate system.

By computing distances between all known colors and our target color, we can identify which one is closest. If it’s necessary to consider non-linear properties within our search for similar colors based on proximity alone - such as when using more advanced algorithms like CIEDE2000 - then these methods may improve accuracy further still; however most cases should be satisfied with simple proximity-based calculations alone.

In this example, the colors obtained from the image also include an alpha channel, which represents transparency. We can obtain its value between 0 and 1 (corresponding to CSS rgba’s alpha value) by dividing alpha by 255. If it is equal to zero, then regardless of its RGB values, it will be a fully transparent color.

getColor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function getColor(r, g, b, a) {
if (a === 0) {
return {
name: "Transparent",
name_zh: "透明色",
r: null,
g: null,
b: null,
a: null,
hex: null,
};
}
const colorList = [];
let minDistance = 1000000;
let res = null;
for (let i = 0; i < colorList.length; i++) {
const distance =
Math.pow(r - colorList[i].r, 2) +
Math.pow(g - colorList[i].g, 2) +
Math.pow(b - colorList[i].b, 2);
if (distance <= minDistance) {
minDistance = distance;
res = colorList[i];
}
}
return res ? { ...res, a } : null;
}

User Interface


On the user interaction interface, a canvas is placed in the middle area to display the image that needs to be color picked. It is worth noting that our image needs to be fully displayed on the canvas without being stretched or compressed. Using the following method, we can display an image on the canvas.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
function init(url) {
this.selectedColor = null;
const img = new Image();
img.crossOrigin = "true";
img.src = url;

function drawImageScaled(img, ctx) {
const canvas = ctx.canvas;
const hRatio = canvas.width / img.width;
const vRatio = canvas.height / img.height;
const ratio = Math.min(hRatio, vRatio);
const centerShift_x = (canvas.width - img.width * ratio) / 2;
const centerShift_y = (canvas.height - img.height * ratio) / 2;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(
img,
0,
0,
img.width,
img.height,
centerShift_x,
centerShift_y,
img.width * ratio,
img.height * ratio
);
}

img.onload = () => {
const canvas = this._.refs.canvas;
const ctx = canvas.getContext("2d");
drawImageScaled(img, ctx);
this.dominantColor = getColor(...colorThief.getColor(img), 1);
this.palette = colorThief
.getPalette(img, 6)
.map((color) => getColor(...color, 1));
};
img.onerror = () => {
alert(
"Error occored on loading your image, you may need to try with another image"
);
};
}

Then we add event listener on the input above our canvas.

1
2
3
4
5
6
7
onInputFile(e) {
const file = e.target.files[0];
if (file) {
const url = URL.createObjectURL(file);
this.init(url);
}
}

To simplify user operation costs, we can also monitor page drag and drop events.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
onDropFile(e) {
e.preventDefault()
const file = e.dataTransfer.files[0]
if (file && file.type.startsWith('image')) {
const url = URL.createObjectURL(file);
this.init(url);
}
}

const events = ['dragenter', 'dragover', 'dragleave']
function preventDefaults(e) {
e.preventDefault()
}
events.forEach((eventName) => {
document.body.addEventListener(eventName, preventDefaults)
})
document.body, addEventListener('drop', this.onDropFile)

Note that: In order to achieve file drag and drop, we need to use e.preventDefault() to prevent the default behavior of the browser for 4 drag and drop events: dragenter, dragover, dragleave, and drop. (When an image file is dragged into the browser window, the browser will open this image in a new tab by default).

Get Color With Pointer Click

When the pointer clicks on the canvas, we can calculate the position of our click on the canvas through its click event property, and thus obtain the color data of this position.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
onCanvasClick(e) {
const canvas = this._.refs.canvas;
const ctx = canvas.getContext("2d");
const canvasWidth = canvas.clientWidth;
const x = ((e.pageX - canvas.offsetLeft) / canvasWidth) * 1000;
const y = ((e.pageY - canvas.offsetTop) / canvasWidth) * 1000;
const imageData = ctx.getImageData(x, y, 1, 1);
const data = imageData.data;
const colorCount = {};

let maxCount = 0;
let maxColor = "";
let maxColorRGB = "";

for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const a = data[i + 3] / 255;

const color = getColor(r, g, b, a);
const colorHex = getColor(r, g, b, a).hex;

if (colorCount[colorHex]) {
colorCount[colorHex]++;
} else {
colorCount[colorHex] = 1;
}

if (colorCount[colorHex] > maxCount) {
maxCount = colorCount[colorHex];
maxColorRGB = `rgb(${r}, ${g}, ${b}, ${a})`;
maxColor = color;
}
}
this.selectedColor = maxColor;
}

Additional feature: Obtain the main color tone of an image.

Color Thief is a JavaScript library used to extract the dominant color palette from an image. It works by sampling pixels of the image, cutting down on the sampled colors, and finally sorting them based on their frequency to determine the main color palette of the image.

Credits

  1. Color Thief
  2. RGB to Color Name Mapping (Triplet and Hex)

Create a Color Picker With JavaScript

https://lynan.cn/color-picker-with-color-name/

Author

Lynan

Posted on

2023-04-15

Licensed under

Comments