Web Development

D3.js Gradients: The Easy Way

July 14, 2016

Sirius Strebe

By Sirius Strebe

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:

Copy

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);

 

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.

Copy

var line = svg.append("path")
              .attr("d", function () { … } )
              .attr("stroke", "url(#svgGradient)")
              .attr("fill", "none");

 

D3.js path with stroke to gradient
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:

Copy

var line = svg.append("path")
              .attr("d", function () { … } )
              .attr("fill", "url(#svgGradient)");

D3 gradients as a fill

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.

Copy

d3.selectAll("#svgGradient")
  .transition()
  .attr("x1", "100%")
  .attr("y1", "0%")

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

Copy

d3.selectAll("stop")
  .transition()
  .attr("stop-color", "purple")
  .attr("offset", "90%")

Combining these can get some pretty cool results.

Transitions and animations in D3
See full example

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.

D3 rectangular bounding

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

Copy

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

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.

Copy

var gradient = defs.append("radialGradient");
radial gradients
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.

Sirius Strebe

Sirius Strebe

Unless otherwise specified, source code in this post is licensed under a
Creative Commons Attribution 4.0 International license (CC BY 4.0).

You might also like...

7

Feb.

Jeff Dance

10 Factors for Choosing a CMS

There are dozens of Content Management System (CMS) platforms available to to help you manage the content, marketing, and SEO on your website. But with all the good options out there, how do you know how to choose the right CMS? Consider the following 10 factors when choosing your CMS. #1 Price Some CMS licenses start … Continued

4

Jan.

Ben Spencer

You’ve Beaten B.O.C.O. – Now, Deliver Your Product to the World

Over the course of 2018, Fresh’s designers and developers created an interactive digital journey to educate companies and teams on the UX Design Process. In the research, design, and test worlds, users undertook an adventure to beat B.O.C.O., or “The Beast of Conflicting Opinions.” The beginning of 2019 sees B.O.C.O. defeated at last. Now, you … Continued

25

Apr.

Travis Luong

How to Create a Fuzzy Search-as-You-Type Feature with Elasticsearch and Django

Tutorial: How to Create a Fuzzy Search-as-you-type Feature with Elasticsearch and Django Recently, I had to figure out how to implement a fuzzy search-as-you-type feature for one of our Django web APIs. I couldn’t find any comprehensive tutorial on how to build this specific feature, so I decided to combine multiple sources and document the … Continued