Color Roller v0.5 is scheduled to be image free.
Overall, this has been relatively painless, though the lack of alpha masks in Gecko and gradient stops in Trident has provided... interest.
The one big problem has been the color wheel. All elements, in all browsers, are meant to be square. Curving corners is one thing. Creating a wheel of conical gradients has been something else entirely.
Preface:
Throughout the ensuing discussion, our goal is to make a cone or wheel using just hues, as follows:
Img1
While the color wheel used in most color pickers has a white or greyscale center, that is best actualized by adding a radial gradient after the actual wheel is created - trying to factor the gradient into the creation of the wheel is needlessly complicated.
Here goes.
CSS:If it were possible to use style-sheets, we would.
CSS3 supports gradients (and even IE is on board with proprietary filers), but only two types: radial and linear.
The radial gradient is made up of an inner and outer color.
Completely useless when trying to form a color wheel in which the gradients are stacked cylindrically.
Img2
The linear gradient isn't much better.
Img3
If there just was a way to pinch the bottom of each square to a point (& round the outer edges), we would have the perfect color wheel!
img4
Interestingly enough, a "pinching" style
does exist. Webkit's
z-perspective allows for everything to be skewed as though one were looking from the center of the side of the element, exactly as we need.
Unfortunately, though, this style is
only supported in Webkit, and
only on the Mac or iPhone. Till I have one of those, this'll have to wait.
[Actually, the way that Webkit (and Canvas) define the radial gradient allows for the inner color to be entirely outside the starting 'outer' circle. This can be used to create some nice conical gradients, but I couldn't work it out reliably. Considering that this syntax is bound for deprecation (I hope) it's just as well. If anyone else researches this, keep us in the loop.]
Canvas:In the browsers that support it, Canvas allows for general pixel manipulation and can be used to draw lines and gradients.
There are three ways that canvas can be used to generate the circle:
- Cycle through the perimeter of a square, drawing lines from the center, incrementing the hue.
- Cycle through all the pixels of a box and set each to the requisite color.
- Create one pixel lines of many hues, than rotate each line on its base, by different angles, to form a circle.
Working backwards.
The third method is entirely not suitable for canvas. For one, canvas rotates on the elements center, with no support for moving the transform-origin to the base of the line (requiring each line to be positioned). Much worse, in order to rotate an element, one must store the canvas in memory, clean it, draw the line, rotate the whole canvas, than redraw the original content. Not a method I would try.
The disadvantage of the second method is that it requires far more math - each pixel has four values that must configured (Red, Green, Blue, and Opacity) and this quickly adds up. A box 100x100 has an array of 40,000 values, a box 500 pixels square requires a million values to be set!
Furthermore, it won't scale well - as the size of the square increases, the array increases exponentially.
And lastly, support for getImageData prior to Gecko 1.9 was incomplete and the alternative was wordier and slower.
Nonetheless, the pixel handling for the image array in canvas is so blazingly fast, that it still works lag free (0.045s with Firefox under load). And when done drawing it is written to the image buffer just like any other image, which saves memory and future handling.
Take a
look at it here (draw function, lines 403-433). Play with it. Test it out.
It's really good.
If you have a need for a larger circle, try drawing one 50x50 and scaling the canvas element.
The third method is decidedly the quickest at 1/5 of 1/10 of a second, and is use in the latest versions of ColorRoller.
However, it too is not perfect: The anti-aliasing feature built into both Firefox and Webkit cause weird artifacts when using this method to draw the circle.
There is
no practical way to shut this;
mozImageSmoothingEnabled did not help.
The only solution we found is to use lines of two or three pixels wide, but this must be offset correctly or the entire image will be skewed by that amount.
Note: The second and third method assume we would fill in the whole square and crop out the circle when finished.
SVG:
The SVG engine is not nearly as efficient as the Canvas engine, and it manipulates its shapes using the DOM instead of drawing to the pixel.
On the other hand, it offers quite a bit of functionality far beyond that available to the Canvas engine.
Some methods:
- Cycle through all the pixels of the circle, covering each with a one pixel SVG square colored as needed.
Same as the canvas method, but using the wrong tool. - Cycle around the perimeter of a square drawing lines of different hues to the center.
Same as the canvas method, but using the wrong tool. - Draw a number of hue lines, then rotate them around the box's center point to generate the wheel.
Same as Canvas, but better for the job. Use transform-origin if available, otherwise position elements as needed.
- Create the six linear gradient boxes, than use the filters to pinch the bottom of each into a point.
This is the correct way, but will require testing:
- feDiffuseLighting with feDisplacementMap or feComposite: This is discussed here, referencing earlier browsers.
- feDisplacementMap to create perspective: Discussed here, with a demo on the page. May be FF only.
- feTurbulence, which is based on the radial filter, has been mentioned as an avenue of investigation.
- ClipPaths that are manipulated to draw the bottom corners together. Discussed here, demoed here.
- Create a text path, and draw the gradient along the text path. Discussed on the same page as #5, but the demo did not work for me.
Of these, the best method is definitely number three. If the perspective filter works, that is especially ideal, as the work to later convert the code to CSS (as z-perspective is implemented) would be minimal.
VML:Vector Markup Language, like many IE-only proprietary standards, actually had potential.
However, being
mostly undocumented and only
partially supported by one browser, it's usefulness is quite limited.
Worse, each version of IE has progressively
less support for any vector markup, so it will probably soon be entirely obsolete.
(Tough luck,
excanvas guys. But a browser with top-notch graphics is not good for a company who's business model can't handle web based competition..)
The methods:
- Same as those for Canvas and SVG (1-3 above).
- VML actually has built in support for conical gradients, but these were not designed to be used stand-alone and are entirely undocumented.
We could not get things to work, so have implemented the same method as we use with canvas - lines from the perimeter to the center.
For those willing to experiment, the following triangle should be a good starting point:
<html xmlns:v=VML> <head><style> v\:*{behavior:url(#default#VML)} </style></head> <body><v:group style=width:50;height:50> <v:shape style=width:1000;height:1000 fillcolor=#f0f stroked=f path="M10000,5000 R, L,5000, ,, 10000,5000 X E"> <v:fill type=gradientradial method="none" focussize=0,1.5 color2="#0f0" colors=".33 #f00;.66 #ff0;" /> <v:path gradientshapeok=t /> </v:shape> </v:group></body> </html>Look at arguments. Hack at it. I can
explain the above VML if it helps.
SMIL:While
SMIL is really about time and vector animations, it obviously must support vectors, and is
somewhat embraced by all browsers.
(OK, more like a
peck on the cheek than a true embrace.)
I haven't a need for this, and it may or may not do the trick, but it is at least worthy of mention.
A note on my understanding of Raphael.
Raphael's
cross browser color wheel uses SVG to do what should be done in Canvas. This together with the attempt to build in the gradient into each line, and using rotation and positioning, makes for a heavy, inflexible experience.
This is not due to a flaw in Raphael, it has to do with generated code. When using a generator, one must expect verbiage, as the methods are not designed for any one application.
But at least it works.