Horizontal Gradient
As usual I will start out by showing off some code and then chew on it a bit:
The utility libraries the implementation depends upon may be found here: graphics.js, math.js.
I decided to split my architecture in two parts: iterating the pixels and the graphics kernel. This arrangement should make it easy to implement various algorithms even beyond just gradients without too much effort.
As seen above the kernel itself is quite simple. It just performs a linear interpolation (lerp) based on x.
Vertical Gradient
Let's have a look at how vertical gradient works out in this scheme next:
Instead of dealing with the x axis, we just interpolate based on y instead. Not much of change really.
I guess the interesting question now is how to deal with linear gradients with arbitrary directions (ie. from corner to corner). It's going to take some extra math to sort out this case. I will show you how to deal with this case next:
Arbitrary Direction
As seen below the code is a bit trickier in this case:
Though the math may look a bit funky and it might be possible to simplify it further, the basic idea is very simple. First I project the current point to a ray formed by given begin and end points. The projection algorithm will find the point closest to the given point within the ray and return it.
There is one special case to keep in mind. As my projection algorithm deals with a ray, it's possible that the returned vector is not between the bounds. In order to deal with this I perform a check based on normals (normal is a vector that has a length of one unit). If the vectors point to opposite directions the sum of their normals will be zero. This is the case I want to avoid since I want my projection vector to be between or after the bounds.
Conclusion
In this brief post I showed how to generate horizontal, vertical and arbitrary linear gradients using HTML5 canvas API and the pixel-wise operations it offers. I will expand upon the idea in the following posts. It might be nice to revisit radial gradient next and see how that works out.
If you are interested in pixel access I suggest referring particularly to this post by beej for further ideas.