Each browser has its own characteristics, such as today’s colorPicker is, a thousand browsers, a thousand Hamlets, a thousand colorpickers. Today’s Canvas series uses canvas to make a colorpicker.

Renderings and demos

Suddenly turn to a colorpicker that was written in JS and DOM before, it’s a bit clunky, just throw a picture (old)

This is really lame and performs poorly because every optional color value is a DOM, and if you tried to implement 256*256, the browser would explode ~~~~~

Okay, back to demo(new)

Win7killer.github. IO /can_ps/ SRC /…

Yes, it is modeled after the color picker in PHOTOSHOP.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

implementation

First of all, let’s look at the effect diagram analysis how to do:

1. On the left side of the colorbar

The left side provides a series of transition colors. It is not difficult to see that this is the six colors of “red, yellow, green, cyan, blue and purple”, which are then processed by transition colors. And then finally the purple transitions back to the red.

In addition, the circular one may be more easily identified, as shown below:

So, we can use the canvas transition color to implement this area on the left,

The code is as follows:

 1 function colorBar() {
 2     var gradientBar = ctx.createLinearGradient(0, 0, 0, width);
 3     gradientBar.addColorStop(0, '#f00');
 4     gradientBar.addColorStop(1 / 6, '#f0f');
 5     gradientBar.addColorStop(2 / 6, '#00f');
 6     gradientBar.addColorStop(3 / 6, '#0ff');
 7     gradientBar.addColorStop(4 / 6, '#0f0');
 8     gradientBar.addColorStop(5 / 6, '#ff0');
 9     gradientBar.addColorStop(1, '#f00'); 10 11 ctx.fillStyle = gradientBar; 12 ctx.fillRect(0, 0, 20, width); 13}Copy the code

When it comes to the fillStyle of canvas or strokenStyle, you can use the transition color object (your blind name). For more information, you can visit W3CSchool.

2. Middle color zone

The middle piece looks easy at first glance, but it’s a bit confusing at third glance.

At first glance: in fact, it is the color selected on the left (such as A), and then the transition process, is still the transition.

Look again: well, color, and then black, white, three colors three angles how to transition ~~~~ (if there is a quick transition to achieve the way please leave a message to inform me, THX).

Three see: so, borrow once, for example red to white, and then add a layer of black to transparent? Yeah, that’s the plan. (I detoured from red to black and white to transparent myself)

So it is with the help of two transition color filling, realize the middle color block area.

The code is as follows:

 1 functionColorBox (color) {2 var gradientBase = ctx.createLinearGradient(30, 0, width + 30, 0); 4 gradientBase.addColorStop(1, color); 5 gradientBase.addColorStop(0,'rgba(255,255,255,1)'); 6 ctx.fillStyle = gradientBase; 7 ctx.fillRect(30, 0, width, width); 10 var my_gradient1 = ctx.createLinearGradient(0, 0, 0, width); 11 my_gradient1.addColorStop(0,'rgba (0,0,0,0)');
12     my_gradient1.addColorStop(1, 'rgba (0,0,0,1)'); 13 ctx.fillStyle = my_gradient1; 14 ctx.fillRect(30, 0, width, width); 15}Copy the code

Note that the first fill is from the landscape. At this time, the left side of the middle block is not the origin of the canvas, so an offset of 30px is added

The second time I fill it, the Y-axis is 0 again.

This should be paid attention to in practical application.

At this point, I’m done drawing on the left canvas.

3. Color selection event processing

First, specify the interaction events:

Select the left colorbar (for example, #ff0), the base color in the middle will change accordingly, and the corresponding color in the upper right corner (#ff0).

Select the color in the middle area, the left side remains unchanged, and the corresponding color value can be obtained to end the interaction.

The final result is to display the selected color in the DOM area on the right.

There are no DOM objects in the canvas, so mouse click events depend on the position of the mouse to determine whether to process them accordingly. And we’re not drawing a PATH object, nor can we use methods like inpath to tell.

Click event code:

 1 can.addEventListener('click'.function(e) {
 2     var ePos = {
 3         x: e.offsetX || e.layerX,
 4         y: e.offsetY || e.layerY
 5     }
 6     var rgbaStr = '# 000';
 7     if (ePos.x >= 0 && ePos.x < 20 && ePos.y >= 0 && ePos.y < width) {
 8         // in
 9         rgbaStr = getRgbaAtPoint(ePos, 'bar');
10         colorBox('rgba(' + rgbaStr + ') '); 11}else if (ePos.x >= 30 && ePos.x < 30 + width && ePos.y >= 0 && ePos.y < width) {
12         rgbaStr = getRgbaAtPoint(ePos, 'box'); 13}else{14return;
15     }
16     outColor(rgbaStr.slice(0, 3).join());
17     cur.style.left = ePos.x + 'px';
18     cur.style.top = ePos.y + 'px';
19     cur.style.outlineColor = (rgbaStr[0] > 256 / 2 || rgbaStr[1] > 256 / 2 || rgbaStr[2] > 256 / 2) ? '# 000' : '#fff'; 20});Copy the code

Among them, getRgbaAtPoint is the final method to obtain the color value. It is necessary to select the left or right image according to different mouse position

It’s easy to get the color, just get the imageData of the corresponding region, and then get the color value of the corresponding position from the color array.

Those who have done canvas pixel processing will understand it better. If you don’t understand it, you are advised to take a look at the getImageData method first

Get the color code:

 1 function getRgbaAtPoint(pos, area) {
 2     if (area == 'bar') { 3 var imgData = ctx.getImageData(0, 0, 20, width); 4}else {
 5         var imgData = ctx.getImageData(0, 0, can.width, can.height);
 6     }
 7 
 8     var data = imgData.data;
 9     var dataIndex = (pos.y * imgData.width + pos.x) * 4;
10     return[ 11 data[dataIndex], 12 data[dataIndex + 1], 13 data[dataIndex + 2], 14 (data[dataIndex + 3] / 255).toFixed(2), 15 ]; 16}Copy the code

This is the value of the RGBA color.

Note that the last data in the alpha channel is 0-255 in the Canvas imageData, not 0-1, so we need to convert it.

Color Output & Conversion:

Once you get the color, you can export it to the right.

The right side only uses RGB three channels, so take the first three digits of the array.

For hex colors, RGB is used to convert.

The conversion code is as follows:

 1 function rgb2hex(rgb) {
 2     var aRgb = rgb instanceof Array ? rgb : (rgb.split(', ') | | [0, 0, 0]); 3 var temp; 4return [
 5         (temp = Number(aRgb[0]).toString(16)).length == 1 ? ('0' + temp) : temp,
 6         (temp = Number(aRgb[1]).toString(16)).length == 1 ? ('0' + temp) : temp,
 7         (temp = Number(aRgb[2]).toString(16)).length == 1 ? ('0' + temp) : temp,
 8     ].join(' ');
 9 }
10 
11 function hex2rgb(hex) {
12     if(hex.length == 3) { 13 hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; 14} 15return[ 16 parseInt(hex[0] + hex[1], 16), 17 parseInt(hex[2] + hex[3], 16), 18 parseInt(hex[4] + hex[5], 16), 19 ].join(); 20}Copy the code

In simple terms, it is the conversion of base 10 to base 16.

For RGB (255,0,255), “ff”, “00”, “ff”, or “#ff00ff” for hex (255,0,255).

Bonus effects:

The middle of the color selection has an effect, that is, where the mouse drag, select the corresponding color.

Mouse drag events we are not unfamiliar, directly on the code, no nonsense

 1 can.addEventListener('mousedown'.function(e) {
 2     var ePos = {
 3         x: e.layerX || e.offsetX,
 4         y: e.layerY || e.offsetY
 5     }
 6     if (ePos.x >= 30 && ePos.x < 30 + width && ePos.y >= 0 && ePos.y < width) {
 7         document.onmousemove = function(e) {
 8             var pos = {
 9                 x: e.clientX,
10                 y: e.clientY
11             }
12 
13             pos.x = pos.x < 30 ? 30 : pos.x && (pos.x > (30 + width - 1) ? (30 + width - 1) : pos.x);
14             pos.y = pos.y < 0 ? 0 : pos.y && (pos.y > (width - 1) ? (width - 1) : pos.y);
15 
16             rgbaStr = getRgbaAtPoint(pos, 'box');
17             cur.style.left = pos.x + 'px';
18             cur.style.top = pos.y + 'px';
19             cur.style.outlineColor = (rgbaStr[0] > 256 / 2 || rgbaStr[1] > 256 / 2 || rgbaStr[2] > 256 / 2) ? '# 000' : '#fff'; 20 outColor(rgbaStr.slice(0, 3).join()); 21}; 22 document.onmouseup =function() { 23 // outColor(rgbaStr.slice(0, 3).join()); 24 document.onmouseup = document.onmousemove = null; 25} 26} 27 28});Copy the code

In this way, each piece of code is put together to form a complete shelf, with the final code attached (long and folded) :

Press Ctrl+C to copy the code

Press Ctrl+C to copy the code

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Write at the end:

In the process of playing after writing the effect, I found that the browser had some problems with canvas transition color implementation. Chrome is obviously a little better than FF.

Figure: according to the truth, the bottom color should be RGB (0,0,0), but it can be seen in the figure, some places are not ~~~

Most of them are still 000, but at some point a channel might have a 1. The cause is unknown.




<! DOCTYPE html> <html lang="zh">

	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title>Document</title>
		<style>
			body {
				background: # 535353;
				padding: 0;
				margin: 0;
			}
			
			canvas {
				cursor: crosshair;
			}
			
			#cur {
				width: 3px;
				height: 3px;
				outline: 2px solid # 535353;
				margin-left: -1px;
				margin-top: -1px;
				position: absolute;
			}
			
			.wrapper {
				position: relative;
			}
			
			#color_show {
				width: 50px;
				height: 50px;
				background: #f00;
			}
			
			.panel {
				width: 200px;
				height: 200px;
				position: fixed;
				top: 20px;
				right: 20px;
				background-color: #fff;
				padding: 10px;
				text-align: center;
				line-height: 2em;
			}
		</style>
	</head>

	<body>
		<div class="wrapper">
			<canvas id="canvas" width="600" height="600"></canvas>
			<em id="cur"></em>
			<div class="panel">
				<div id="color_show"></div>
				<label>
            rgb <input type="text"  class="color_input" value="" id="rgb_value">
        </label><br>
				<label>
            hex <input type="text"  class="color_input" value="" id="hex_value">
        </label>
			</div>
		</div>
		<script>
			(function() {
				var width = 256;
				var can = document.getElementById('canvas');
				var ctx = can.getContext('2d');
				var curColor = 'rgba (255,0,0,1)';
				var cur = document.getElementById('cur');
				var rgbValue = document.getElementById('rgb_value');
				var hexValue = document.getElementById('hex_value');
				var colorShow = document.getElementById('color_show');

				var aColorInput = document.getElementsByClassName('color_input');

				function colorBar() {
					var gradientBar = ctx.createLinearGradient(0, 0, 0, width);
					gradientBar.addColorStop(0, '#f00');
					gradientBar.addColorStop(1 / 6, '#f0f');
					gradientBar.addColorStop(2 / 6, '#00f');
					gradientBar.addColorStop(3 / 6, '#0ff');
					gradientBar.addColorStop(4 / 6, '#0f0');
					gradientBar.addColorStop(5 / 6, '#ff0');
					gradientBar.addColorStop(1, '#f00');

					ctx.fillStyle = gradientBar;
					ctx.fillRect(0, 0, 20, width);
				}

				function rgb2hex(rgb) {
					var aRgb = rgb instanceof Array ? rgb : (rgb.split(', ') | | [0, 0, 0]); var temp;return [
						(temp = Number(aRgb[0]).toString(16)).length == 1 ? ('0' + temp) : temp,
						(temp = Number(aRgb[1]).toString(16)).length == 1 ? ('0' + temp) : temp,
						(temp = Number(aRgb[2]).toString(16)).length == 1 ? ('0' + temp) : temp,
					].join(' ');
				}

				function hex2rgb(hex) {
					if(hex.length == 3) {
						hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
					}
					return [
						parseInt(hex[0] + hex[1], 16),
						parseInt(hex[2] + hex[3], 16),
						parseInt(hex[4] + hex[5], 16),
					].join();
				}

				function putCurDom(color) {
					if(/([0-9a-f]{3}|[0-9a-f]{6})/i.test(color)) {
						// hex
						color = hex2rgb(color);
					} else if(color instanceof Array) {
						color = color.join(', ');
					} else if(/ \ d {1, 3} \ \ d {1, 3}) ({2} / i.t est (color)) {}else {
						return; }}functionVar gradientBase = CTX. CreateLinearGradient (30, 0, width + 30, 0); gradientBase.addColorStop(1, color); gradientBase.addColorStop(0,'rgba(255,255,255,1)'); ctx.fillStyle = gradientBase; ctx.fillRect(30, 0, width, width); Var my_gradient1 = ctx.createLinearGradient(0, 0, 0, width); my_gradient1.addColorStop(0,'rgba (0,0,0,0)');
					my_gradient1.addColorStop(1, 'rgba (0,0,0,1)');
					ctx.fillStyle = my_gradient1;
					ctx.fillRect(30, 0, width, width);
				}

				function init() {
					colorBar();
					colorBox(curColor);
					bind(a); }function bind() {
					can.addEventListener('click'.function(e) {
						var ePos = {
							x: e.offsetX || e.layerX,
							y: e.offsetY || e.layerY
						}
						var rgbaStr = '# 000';
						if(ePos.x >= 0 && ePos.x < 20 && ePos.y >= 0 && ePos.y < width) {
							// in
							rgbaStr = getRgbaAtPoint(ePos, 'bar');
							colorBox('rgba(' + rgbaStr + ') ');
						} else if(ePos.x >= 30 && ePos.x < 30 + width && ePos.y >= 0 && ePos.y < width) {
							rgbaStr = getRgbaAtPoint(ePos, 'box');
						} else {
							return;
						}
						outColor(rgbaStr.slice(0, 3).join());
						cur.style.left = ePos.x + 'px';
						cur.style.top = ePos.y + 'px';
						cur.style.outlineColor = (rgbaStr[0] > 256 / 2 || rgbaStr[1] > 256 / 2 || rgbaStr[2] > 256 / 2) ? '# 000' : '#fff';
					});

					can.addEventListener('mousedown'.function(e) {
						var ePos = {
							x: e.layerX || e.offsetX,
							y: e.layerY || e.offsetY
						}
						if(ePos.x >= 30 && ePos.x < 30 + width && ePos.y >= 0 && ePos.y < width) {
							document.onmousemove = function(e) {
								var pos = {
									x: e.clientX,
									y: e.clientY
								}

								pos.x = pos.x < 30 ? 30 : pos.x && (pos.x > (30 + width - 1) ? (30 + width - 1) : pos.x);
								pos.y = pos.y < 0 ? 0 : pos.y && (pos.y > (width - 1) ? (width - 1) : pos.y);

								rgbaStr = getRgbaAtPoint(pos, 'box');
								cur.style.left = pos.x + 'px';
								cur.style.top = pos.y + 'px';
								cur.style.outlineColor = (rgbaStr[0] > 256 / 2 || rgbaStr[1] > 256 / 2 || rgbaStr[2] > 256 / 2) ? '# 000' : '#fff';
								outColor(rgbaStr.slice(0, 3).join());
							};
							document.onmouseup = function() { // outColor(rgbaStr.slice(0, 3).join()); document.onmouseup = document.onmousemove = null; }}}); }function outColor(rgb) {
					rgbValue.value = rgb;
					hexValue.value = rgb2hex(rgb);
					colorShow.style.backgroundColor = 'rgb(' + rgb + ') ';
				}

				function getRgbaAtPoint(pos, area) {
					if(area == 'bar') {
						var imgData = ctx.getImageData(0, 0, 20, width);
					} else {
						var imgData = ctx.getImageData(0, 0, can.width, can.height);
					}

					var data = imgData.data;
					var dataIndex = (pos.y * imgData.width + pos.x) * 4;
					return [
						data[dataIndex],
						data[dataIndex + 1],
						data[dataIndex + 2],
						(data[dataIndex + 3] / 255).toFixed(2),
					];
				}

				init();
			})()
		</script>
	</body>

</html>Copy the code