Is it possible to greate a linear gradient which contains all possible RGB colors (only red, green, blue - no alpha values) for a linear color picker.
So far I've tried it with the following gradient, but it doesn't contain all values from rgb(0,0,0)
to rgb(255,255,255)
:
var grd = ctx.createLinearGradient(0, 0, width, 0);
grd.addColorStop(0, 'red');
grd.addColorStop(1 / 6, 'orange');
grd.addColorStop(2 / 6, 'yellow');
grd.addColorStop(3 / 6, 'green')
grd.addColorStop(4 / 6, 'aqua');
grd.addColorStop(5 / 6, 'blue');
grd.addColorStop(1, 'purple');
Any help is highly appreciated.
Unfortunately no you can't.
The main reason is that 24-bit RGB contains 16,777,216 number of colors (2563). Just to get an estimate you will will need a screen resolution that is 4096 x 4096 pixels (of course there is no such square screen, but the equivalent would be in about 16:9 format of that for an actual monitor).
Simply put: there is no room to place all the pixels on a normal screen.
In addition you will get problem using a gradient as this goes only in one direction for canvas. You would need to plot the color in two directions which you need to do manually manipulating the bitmap directly.
My suggestion is to take a screen snapshot of an existing such palette and draw that image onto the canvas. This will quantize the colors (this also happens in all available color pickers) but will give you a close approximation to the color you need.
In addition you can add sliders to fine-adjust the value as well as text boxes (the latter is a bit complicated in pure canvas, but you can make a combination of html and canvas to achieve this).
An approximation representing "all" colors could be like this (snapshot from demo below):
Update
Ok, I promised to get back to you about calculating the position from a RGB value. Starting in a palette as in the image you can easily calculate the position by converting the color to HSV color space.
The function to do RGB to HSV looks like this:
function rgb2hsv() {
var rr, gg, bb,
r = arguments[0] / 255,
g = arguments[1] / 255,
b = arguments[2] / 255,
h, s,
v = Math.max(r, g, b),
diff = v - Math.min(r, g, b),
diffc = function (c) {
return (v - c) / 6 / diff + 1 / 2;
};
if (diff === 0) {
h = s = 0;
} else {
s = diff / v;
rr = diffc(r);
gg = diffc(g);
bb = diffc(b);
if (r === v) {h = bb - gg}
else if (g === v) {h = (1 / 3) + rr - bb}
else if (b === v) {h = (2 / 3) + gg - rr};
if (h < 0) {h += 1}
else if (h > 1) {h -= 1}
}
return {
h: (h * 360 + 0.5) |0,
s: (s * 100 + 0.5) |0,
v: (v * 100 + 0.5) |0
}
};
Now color is represented in degrees (0-359), saturation (0-100) and luminance (0-100).
To get horizontal position all you need to do is to divide width of your palette on 360 and multiply with h (hue). To get vertical position you split the palette in upper part and lower part. If saturation is < 100 then you're in the upper part, if V < 100 then you're in lower part:
function getPos(canvas, h, s, v) {
var m = canvas.height / 2,
x = canvas.width / 360 * h,
y;
if (s === 100 && v === 100) {
y = m;
} else if (v === 100 && s < 100) {
y = m / 100 * s;
} else if (s === 100 && v < 100) {
y = m / 100 * (100 - v) + m;
}
x = (x + 0.5) |0; //convert to integer
y = (y + 0.5) |0;
};
This will of course require that the palette you generate is very accurate.
Notice the cursor is not set based on mouse position, but on RGB (HSV) value. It first picks a color RGB from mouse position, then converts it to HSV and calculate the position from that.
The palette is generated dynamically in relation to window size.
Also worth to mention for plain color picking, is that the problem with gradients that seems smooth is that they are dithered. This means that you can get a pixel value that apparently is not in the range you're picking from.
To reduce this problem when you do a color pick, you will need to average the area around the x and y position you get from the mouse.