Article

D3.js Gradients: The Right and The Easy Way

The Right Way

I was recently searching for a technique to apply a gradient to a path created using D3.js. The first page of Google results yielded several examples pioneered by Mike Bostock, creator of D3 itself. Take a look at Gradient Along Stroke, for example.

Mike’s example splits a path into several hundred subsections, each with its own color value, to simulate a gradient along the path.

After an hour or two of wrapping my head around this example filled with complex geometry and vector mathematics, I thought to myself that there must be an easier way. Some digging around revealed that an easier way does in fact exist. However, like anything easy, it does have its drawbacks.

The Easy Way

D3.js is designed to easily render SVG, with its own built-in technique for creating gradients. SVG first requires you to create the gradient definition before you can use it, and it can be a little verbose:

[code]
var svg = d3.select(“body”).append(“svg”)
.attr(“width”, 500)
.attr(“height”, 300);

var defs = svg.append(“defs”);

var gradient = defs.append(“linearGradient”)
.attr(“id”, “svgGradient”)
.attr(“x1”, “0%”)
.attr(“x2”, “100%”)
.attr(“y1”, “0%”)
.attr(“y2”, “100%”);

gradient.append(“stop”)
.attr(‘class’, ‘start’)
.attr(“offset”, “0%”)
.attr(“stop-color”, “red”)
.attr(“stop-opacity”, 1);

gradient.append(“stop”)
.attr(‘class’, ‘end’)
.attr(“offset”, “100%”)
.attr(“stop-color”, “blue”)
.attr(“stop-opacity”, 1);
[/code]

This creates a linear gradient element with the top-left corner set to red and the bottom-right corner set to blue. Placing the gradient in a “defs” tag will tell SVG that it’s a resource, not an element, meaning before we can see it, we need to apply it to an element. So, let’s create a path and set its stroke to our gradient.
[code]
var line = svg.append(“path”)
.attr(“d”, function () { … } )
.attr(“stroke”, “url(#svgGradient)”)
.attr(“fill”, “none”);
[/code]

See the full example

This is the easy way of applying a gradient to a path.

Our gradient was given an ID of “svgGradient,” and we can refer to it using the convention url(#svgGradient). You can apply the gradients to multiple elements and to any property that takes a color value. For example, the same line as above, but as a fill:

[code]
var line = svg.append(“path”)
.attr(“d”, function () { … } )
.attr(“fill”, “url(#svgGradient)”);
[/code]

Transitions and Animations

There is no doubt D3 animations make ordinary charts look awesome. Thankfully, SVG gradients play well with transitions and animations. Instead of applying a transition to the path, you can apply transitions onto the gradient element directly.

[code]
d3.selectAll(“#svgGradient”)
.transition()
.attr(“x1”, “100%”)
.attr(“y1”, “0%”)
[/code]

Or, you can apply a transition onto the “stop” elements that define the colors used in the gradient.

[code]
d3.selectAll(“stop”)
.transition()
.attr(“stop-color”, “purple”)
.attr(“offset”, “90%”)
[/code]

The Downsides

Mike Bostock’s example above shows a gradient being applied to a path along its length, even as the path overlaps itself. Using the easy way, this is impossible. SVG linearGradients are applied as a rectangle, with the start and end points specified in the Cartesian space. This is easily seen by Right-click–>Inspect on the path element. You can see the rectangular bounding box applied to the path.

Looking back to our gradient definition, we specified an x1, x2, y1, and y2:

[code]
var gradient = defs.append(“linearGradient”)
.attr(“id”, “svgGradient”)
.attr(“x1”, “0%”)
.attr(“x2”, “100%”)
.attr(“y1”, “0%”)
.attr(“y2”, “100%”);
[/code]

Changing these values will change where in the rectangle the gradient starts and ends, but as always, it must be within a rectangular bounding box.

Radial gradients are the sibling to linearGradients, and will paint the gradient from center to the outsides in a circular fashion.

[code]
var gradient = defs.append(“radialGradient”);
[/code]

See the full example

Another Option

One last option is to use the gradient attribute gradientTransform. You may provide any svg transform definition, giving you a great deal of flexibility in how you can implement your gradients.

Applying a gradient to an element’s stroke or fill is just one way of giving color to your elements, but the easy way is not always the best way. If your needs are more specific, say your path overlaps itself and your gradient must follow the curve, it might make sense to follow Mike Bostock’s example to create your gradient the “right” way.