Using SVG gradient masks with D3.js

If you’re reading this you’ll probably know what D3.js is. For anyone that doesn’t, D3.js is a great piece of JavaScript software that allows you to do some pretty awesome stuff – generally involving tons of data. It makes great use of HTML, SVG and CSS – nothing more. Neat, right?

The majority of D3.js use cases will obviously involve data representation. But what is data representation without some eyecandy goodness? You need that!

Well, in our last project we extensively used D3.js to show a lot of data, but we also needed some related graphic elements that users could zoom in and out without losing render quality or look weird. So, we decided to implement these directly in our SVG, using our beloved D3.js.

The design

Our design looked something like that (a picture is worth a thousand words):

image

It was clear we needed some sweet gradient masking using SVGs. So, how could we do that? It turned out it’s very simple.

The solution

Starting from a placeholder div for our SVG (let’s say with the id ‘svg-container’), we appended the two necessary elements for our mask in a <defs> tag inside the SVG: the vertical gradient (alpha-opaque-alpha) and the actual mask.

// Handy vars for our banner dimensions
var bannerWidth  = 1500,
    bannerHeight = 500;

// Let's append the SVG element.
d3.select('#svg-container')
    .append('svg:svg').attr('class', 'my-supah-svg')
    .append('svg:defs')
    .call(function (defs) {
      // Appending the gradient
      defs.append('svg:linearGradient')
        .attr('x1', '0')
        .attr('x2', '0')
        .attr('y1', '0')
        .attr('y2', '1')
        .attr('id', 'supah-gradient')
        .call(function (gradient) {
          gradient.append('svg:stop')
            .attr('offset', '0%')
            .attr('stop-color', 'white')
            .attr('stop-opacity', '0');
          gradient.append('svg:stop')
            .attr('offset', '50%')
            .attr('stop-color', 'white')
            .attr('stop-opacity', '1');
          gradient.append('svg:stop')
            .attr('offset', '100%')
            .attr('stop-color', 'white')
            .attr('stop-opacity', '0');
        });
      // Appending the mask
      defs.append('svg:mask')
        .attr('id', 'gradient-mask')
        .attr('width', bannerWidth)
        .attr('height', bannerHeight)
        .attr('x', 0)
        .attr('y', 0)
        // This is the rect that will actually
        // do the gradient masking job: as you can see it's
        // filled with the linear gradient.
        .call(function(mask) {
          mask.append('svg:rect')
            .attr('width', bannerWidth)
            .attr('height', bannerHeight)
            .attr('fill', 'url(#supah-gradient)')
        });
    })

These two elements formed our mask element. We just needed to apply it on our desired element – below there’s an example <rect> element with our mask on:

// Masking on a rect filled with a solid color
d3.select('.my-supah-svg').append('svg:rect')
  .attr('class', 'final-rect')
  .attr('width', bannerWidth)
  .attr('height', bannerHeight)
  .attr('x', 0)
  .attr('y', 0)
  .attr('fill', 'salmon')
  .attr('mask', 'url(#gradient-mask)');

We also needed to apply the masking on image elements – here’s another example for it:

// Masking on an actual image
d3.select('.my-supah-svg').append('svg:image')
  .attr('class', 'final-bg')
  .attr('xlink:href', 'http://lorempixel.com/output/technics-q-g-1500-500-4.jpg')
  .attr('width', bannerWidth)
  .attr('height', bannerHeight)
  .attr('x', 0)
  .attr('y', 0)
  .attr('mask', 'url(#gradient-mask)');

Easy peasy!

Just a little heads up: the mask element must have the same dimensions and position of the element being applied on. In our examples we set it to a fixed width/height and a 0 X/Y.

Conclusion

D3.js is a great, fun tool that can also be used for presentational purposes other than for serious and austere data graphs. Have fun!

Oh, and here you have the interactive demo on codepen!

Leave a Reply

wpDiscuz