CSS Gradients via Canvas
a current project at Shepherd Interactive, certain page elements were designed with background gradients. Given the desire to minimize the need for externally-loaded background images wherever possible, I thought this would be a great opportunity to play around with WebKit's proposed CSS Gradients, which are natively supported by Safari, Chrome, and other WebKit-based browsers. In being a WebKit proposal, however, CSS Gradients are not (yet) natively supported in other rendering engines as used by Firefox, Opera, and Internet Explorer.
CSS Gradients via Canvas provides a subset of WebKit's CSS Gradients proposal for browsers that implement the HTML5
canvas element. To use, just include css-gradients-via-canvas.js (12KB) anywhere on the page. Unlike WebKit, this implementation does not currently allow gradients to be used for border images, list bullets, or generated content. The script employs
document.querySelectorAll()—it has no external dependencies if this function is implemented; otherwise, it looks for the presence of jQuery, Prototype, or Sizzle to provide selector-querying functionality.
The implementation works in Firefox 2/3+ and Opera 9.64 (at least). Safari and Chrome have native support for CSS Gradients since they use WebKit, as already mentioned. Beginning with version 3.6, CSS Gradients are also natively supported by Firefox and this implementation will defer in such case; note that you will need to specify two separate
background CSS properties, one with
-webkit-gradient and another with
-moz-linear/radial-gradient which has a different syntax). This implementation does not work in Internet Explorer since IE does not support Canvas, although IE8 does support the
data: URI scheme, which is a prerequisite (see support detection method).
When/if Gears's Canvas API fully implements the HTML5 canvas specification, then this implementation should be tweakable to work in IE8. In the mean time, rudimentary gradients may be achieved in IE by means of its non-standard gradient filter.
CSS Gradients via Canvas works by parsing all stylesheets upon page load (
DOMContentLoaded), and searches for all instances of CSS gradients being used as background images. The source code for the external stylesheets is loaded via
XMLHttpRequest—ensure that they are cached by serving them with a far-future Expires header to avoid extra HTTP traffic. The CSS selector associated with the gradient background image property is used to query all elements on the page; for each of the selected elements, a canvas is created of the same size as the element's dimensions, and the specified gradients are drawn onto that canvas. Thereafter, the gradient image is retrieved via
canvas.toDataURL() and this data is supplied as the
background-image for the element.
A few notes regarding interactivity with this implementation: CSS gradients will not be applied to elements dynamically added after
DOMContentLoaded. Additionally, each element that has a CSS gradient applied to it gets assigned a method called
refreshCSSGradient(); at any time, this method may be invoked to redraw the gradient on a given element. This is especially useful (and necessary) when an element's size dynamically changes, for example as the result of some user interaction. Likewise, it is important to note that it will not work to rely on event handlers to invoke
refreshCSSGradient() on elements whose style is changed by CSS rules with pseudo-selectors like
:active; this is because event handlers are fired before the rule's style changes are applied to the element. Toggling an element's class name by scripting is how you can assure that its style will be changed before calling
- Updated license to be GPL/MIT dual license instead of just GPL.
<dt>1.3 (<time>2010-03-09</time>): </dt> <dd>Detecting native support in Firefox 3.6; it had only been detecting support for 3.6 alpha, which had significantly different syntax. I ported the linear gradient examples over to use the new native Firefox syntax, but am still working on the radial gradients; the syntax has changed a lot!</dd> <dt>1.2 (<time>2009-09-30</time>): </dt> <dd>Phong Nguyen raised an <a href="http://weston.ruter.net/projects/css-gradients-via-canvas/#comment-9539" title="This is pretty nice – except for one major issue I’ve seen. I’m using the jQuery UI library and it includes a fairly large CSS file (nearly 1700 lines!) that causes the forEach(document.styleSheets … ) loop to take a good long while to finish. This blocks the user from doing anything to the page – in my case, for 2-3 seconds. That’s pretty annoying for any user to have to deal with. Is there some way to speed up that core loop? (Failing that, I could try and detect if I’m loading certain large files and ignore them).">excellent point</a> in that stylesheets which don't contain any CSS Gradients should be ignored in order to improve performance (in his case, for example, the jQuery UI stylesheets are large and don't need to be parsed). Now you can add <code>class="no-css-gradients"</code> to any <code>style</code> or <code>link</code> element and that will prevent this script from looking for CSS Gradients to apply with canvas.</dd> <dt>1.1 (<time>2009-08-12</time>): </dt> <dd>Now if <code>cssGradientsViaCanvas.useCache</code> is set to <code>true</code>, the CSS rules containing gradients are cached in <code>sessionStorage</code> instead of having to be re-parsed out of the stylesheets each time a page loads. For this to work, there must be implementations of <code>JSON.stringify()</code> and <code>JSON.parse()</code> available (e.g. <a href="http://www.json.org/json2.js">json2.js</a>).</dd> <dd>Ability to use <code>data:</code> URIs for images is not explicitly detected since testing for the presence of <code>canvas.toDataURI()</code> is sufficient.</dd> <dt>1.0.3 (<time>2009-08-10</time>): </dt> <dd>Detecting support for native support for CSS Gradients in Firefox 3.6</dd> <dt>1.0.2 (<time>2009-08-05</time>): </dt> <dd>Now requiring that <code>gradient(…)</code> only be used with the <code>background-image</code> property instead of with the <code>background</code> shorthand properties since the additional <code>background-*</code> properties are not parsed out.</dd>
(Since I started redirecting from my blog to GitHub, I've archived the old comments I had received there.)