Mar/070
Color Picker Example.
I just realized that WordPress renames files with more than one dot in the name so my color picker examples were not working as they could not find the MooTools file. I have fixed the renamed file issue so all of the examples are now working.
You can check the most recent version of the Canvas based Color Picker here.
See the color picker articles here:
Enhancing the JavaScript Color Picker with the Canvas element
Writing a Color Picker in JavaScript
Mar/071
Enhancing the JavaScript Color Picker with the Canvas element
With the previous implementation of the Color Picker I used div elements to make the individual “pixels” of the color picker, attaching an event to each div element. This incurs quite a bit of overhead, causing the color picker to perform poorly especially on slower systems. The most obvious way to create something like a color picker efficiently is to use the Canvas element.
First, I needed to detect whether the browser supports the Canvas element. I do that by creating a Canvas element and then seeing if it will return a context like so:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 testcanvas = document.createElement("canvas");
if(testcanvas.getContext){
this.supportsCanvas = true;
delete testelement;
}else{
this.supportsCanvas = false;
delete testelement;
}
Next, in the constructor I needed to create Canvas elements instead of div containers for the Hue Bar and SV Box. Here is a snip of code for the Hue bar:
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 if(this.supportsCanvas){
//create the canvas Saturation Value Box.
this.HueBar = new Element("canvas");
this.HueBar.setStyle("cssFloat", "left");
this.HueBar.setStyle("styleFloat", "left");
this.HueBar.setStyle("margin-right", "5px");
this.HueBar.width=30;
this.HueBar.height=360;
this.HueBar.addEvent("click", this.setCurrentHue.bind(this));
this.Container.appendChild(this.HueBar);
}else{
//create the container for the hue bar.
this.HueBar = new Element("div");
this.HueBar.setStyle("width", "30px");
this.HueBar.setStyle("cssFloat", "left");
this.HueBar.setStyle("styleFloat", "left");
this.HueBar.setStyle("margin-right", "5px");
this.Container.appendChild(this.HueBar);
}
Note: in the case of using the Canvas element, the events are attached to the canvas instead of child elements.
The next difference is in the drawHueBar and drawSVBox functions. In the drawHueBar function, we figure out the multiplier we will need for get steps between 0 and 360 for the height of our bar. Create a new color that is at hue 0 and full saturation and brightness. Get a 2d context to the HueBar canvas element. Iterate over the height of the HueBar. Set the hue for the current position on the HueBar. Get the Hex color string for the new Hue. Set the Canvas Context’s fill style to the Hex color string. Then, fill a Rect that is as wide as the HueBar and 1 pixel tall with that color. Rinse and repeat until we have iterated the whole HueBar.
Here is a snip of code from the drawHueBar function:
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 drawHueBar: function (){
if(this.supportsCanvas){
hSteps = 360 / this.HueBar.height;
hColor = new Color([0,100,100], 'hsb');
myCTX = this.HueBar.getContext('2d');
strhHex = "";
for(hi = this.HueBar.height; hi > 0; hi--){
hColor.changeHue(hi*hSteps);
strhHex = hColor.rgbToHex();
myCTX.fillStyle = strhHex;
myCTX.fillRect(0, this.HueBar.height-hi, this.HueBar.width, 1);
}
}else{
hsvColor = new Color([0,100,100], 'hsb');
fcoDiv = new Element("div");
fcoDiv.setStyle("width","30px");
fcoDiv.setStyle("height","1px");
...
}
}
For the SVBox, it is mostly the same, changing the Brightness in the loop instead of the Hue. I make use of the Canvas element’s gradient fill functions to simplify the work. Inside the loop over the height of the SVBox, we create a new gradient that goes from the left side of the SVBox to the Right. We add a color stop to the linear Gradient that is just a gray value between white and black based on the current position in the height of the SVBox. Then we add a color stop that is based on the current iterations HSV value. Set the Canvas context’s fill style to the gradient and then fill one row of the SVBox.
myLinearGrad = myCTX.createLinearGradient(0, 0, this.SVBox.width, 0);
myLinearGrad.addColorStop(0, "rgb("+vi+","+vi+","+vi+")");
myLinearGrad.addColorStop(1, strsvHex);
myCTX.fillStyle = myLinearGrad;
In the case of using the Canvas element, the UpdateSVBox code does the exact same thing as the drawSVBox.
Getting the color clicked on is a little different and we need to deal with the DOM a little more. In order to get the color clicked on we need to call the getImageData(X, Y) function from a Canvas context object. The issue with that is the event.ClientX and the event.ClientY that is passed in is in the window coordinate space, not our canvas elements coordinate space, so we have to translate. In the event call back functions we need to do the following to get the colors from the Canvas elements. Get a 2d context to the Canvas element. Call the MooTools Element.getCoordinates() function to get the left and top of the canvas in the Window coordinate space. Then, for each axis subtract the Canvases left or top from the clientX or clientY then add the window.scrollX or window.scrollY to deal with the page being scrolled if it is. Pass the resulting X and Y into the context’s getImageData function to get an imageData object for the pixel. The imageData.data array will contain the RGB values for the pixel.
myCTX = this.HueBar.getContext('2d');
hBoxCoords = this.HueBar.getCoordinates();
myImageData = myCTX.getImageData(e.clientX - hBoxCoords.left + window.scrollX, e.clientY - hBoxCoords.top + window.scrollY, 1, 1);
CurHueColor = new Color([myImageData.data[0], myImageData.data[1], myImageData.data[2]]);
With this update the Color Picker is significantly faster with a capital ‘S’, unless you are using IE and then it is just as slow as before. (A fact which totally burns my britches, since IE has such a large portion of the browser market share. I will have to figure out something to do about that in the IE case.)
See the code for the whole Canvas enhanced ColorPicker class
Canvas based Color Picker example
This code was tested in IE 7 and Firefox 2. The Canvas element was not added to Firefox until version 2, no Canvas element in IE.
Mar/071
Writing a Color Picker in JavaScript
I thought it would be cool to have a color picker for a number of dynamic web applications, so I decided to go about making one. What follows here is the basics of the first iteration of a color picker. This is by no means the most efficient that it could be, certainly not for browsers that support the canvas element. This does cover the basic ideas of creating a color picker and provides a good base to build on.
I decided to use MooTools for their Class, Element and Color objects. The Class object and functions are invaluable for writing object oriented JavaScript. The Element object is a great time saver for dealing with DOM Elements and the Color object has all the functionality built in for dealing with HSV (HSB in MooTools).
Some tips that I came across:
In general, I try to create an element once and then clone it when I need many of the same element. One thing that I ran into, that I did not know, is that the DOM cloneNode does not clone events, nor does the MooTools Element.clone() member function. As a result, you have to attach events to each cloned element.
MooTools does not encapsulate all differences between Mozilla-based browsers and IE. Specifically, I ran into this with the event object that is passed into my call back functions. In FireFox, the element clicked is event.target and in IE it is event.srcElement. I am not dissing MooTools there, I have not looked to see if there is even a way for them to deal with that difference seamlessly.
The MooTools documentation on the Color object does not specify the ranges it uses for HSV or RGB. Not all people will know that HSV ranges are different that RGB; specifically, in HSV, hue has a rage from 0 – 360 and both Saturation and Value have a range of 0 – 100%.
Another difference between IE and other standards-based browsers to make note of is the JavaScript notations for float, since float is a reserved word in JavaScript there needs to be a different name for the CSS property. The standard is cssFloat and the Microsoft/IE way is styleFloat.
Ok, now on to the code.
One of the first things I did here is add three members to the MooTools Color class. The MooTools Color class’s setHue, setSaturation and setBrightness functions return a clone of the color object with the new color set. As the color picker’s code will be changing colors over a thousand times per click on the Hue bar I would rather not incur the cost of the clone. Below is the code to add three member functions (changeHue, changeSaturation, changeBrightness) to the color class that change the color values of the color object and return nothing.
The Color Picker consists of a number of elements to facilitate the visual selection of color. On the left is the Hue Bar, which displays all 360 hues at full Value(Brightness) and Saturation, clicking on a Hue will update the SV Box. Just to the right of the Hue bar is the SV Box (Saturation Value Box). The SV Box displays the range of Saturation and Value for a given Hue. In this example we only display a sub set of the Saturation Value combinations that are possible for performance reasons. Hovering over a “Pixel” in the SV Box will display that color in the Preview box and clicking a “Pixel” will set that as the Color Pickers current color, update the Selected color box as well as the HSV and RGB text boxes. To the right of the SV Box is the Selected Color, Preview Color, HSV text boxes, RGB text boxes and the Ok and Cancel buttons. Changing any value in the HSV or RGB text boxes will update the Selected color and the respective color text boxes it the other color space. Clicking the Ok and Cancel buttons will call the associated Ok and Cancel functions, if no Ok or Cancel call back functions have been specified nothing will happen.
The code in the following link is commented so you should be able to follow everything that is going on in the class.
This code has been tested in IE 7 and FireFox 2, if you encounter any issues in other browsers I would be interested in hearing about them especially if you have a solution as well.