This time I will start out by explaining the math behind my approach first and move to the code after that.
Math
I decided to mimic the API of the original gradient as I found it quite versatile. This means that it should be possible to define a radial gradient by defining it using two circles and their colors. The gradient is then drawn using this information. The following image shows the basic idea:In order to come up with a gradient, there has to be some way to interpolate between values. In linear gradients seen before this was quite easy. In the simplest case all we had to do was to vary interpolation factor based on some coordinate (ie. in order to come up with a horizontal gradient, the factor may be derived based on the x coordinate of the current pixel and its relation to the image width).
In this case the interpolation is done between two circles, innerCircle and outerCircle. "point" represents the location of current pixel being processed. I use this information to come up with intersection points (innerIntersection, outerIntersection) and then figure out the interpolation factor based on the point's relation to inner and the whole distance between those two intersection points.
I will show how this idea translates to some actual code next:
Code
As before I used a couple of library files and extended them to fit the demands of this particular task. You may find them here: graphics.js and math.js .
Here's the basic algorithm:
In addition to the idea above I decided to crop the gradient so that it's contained within the outer circle. Hence the check for containment (!outerCircle.contains(point)).
There is also a check that figures out which intersection points to use for figuring out the interpolation ratio. In the case of the image there are actually four. I devised simple logic for coming up with the right pair. Just picking the closest intersection of the inner circle and the farthest intersection of the outer circle compared to that does the trick.
Here's a sample render of the result:
Conclusion
Compared to the native radial gradient this implementation is slow. It does allow several interesting tweaks to be made, however. The basic idea should work with other shapes as well.