svg-filter

View on Github

image/svg+xml

Try clicking on the triangle above. Each time you click a new filter is randomly generated and applied to the svg.

Installation

$ npm install svg-filter

Introduction

This library makes it easy to work with SVG filters. The API is designed to look familiar to d3.js users.

Several of the following examples are re-implementations or slight modifications of examples from this article on smashing magazine by Dirk Weber.

This library exposes all filter types available in the svg spec. For more detailed documentation on that see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/filter.

Now with that out of the way, let's make our first filter:

Blur

Without Filter

With Filter

var d3 = require('d3');
var SvgFilter = require('svg-filter');

// drawRect is just a function that uses d3
// to draw a blue rectangle on the screen.
var drawRect = function (selector) {
  var size = 100;
  return d3.select(selector)
      .append('svg')
          .attr('width', size * 2)
          .attr('height', size * 2)
        .append('rect')
            .attr('width', size)
            .attr('height', size)
            .attr('x', size / 2)
            .attr('y', size / 2)
            .attr('fill', 'blue');
};


// both rectangles are drawn to the screen
var leftRect = drawRect('.left.svg-example-1')
var rightRect = drawRect('.right.svg-example-1');

//
// Now we will create a filter and add
// it to one of the rectangles.
//

// this instantiates a new filter object.
var filter = new SvgFilter();


// A filter won't do anything until you
// add an effect to it. In this case I add the
// `blur` effect and set its standard deviation
// parameter to 5
filter
  .append('blur')
      .attr('stdDeviation', 5);


// attach the filter to the rectangle on the right
rightRect.attr('filter', filter);

Not all filters operate on their input in the same way. For example, a filter can be used to generate turbulence independent of the svg element it is filtering:

Noise

Without Filter

With Filter

var SvgFilter = require('svg-filter');

// This is the same drawRect function that was
// used in the blur example above.
var drawRect = require('../shortcuts/draw-rect');

leftRect = drawRect('.left.svg-example-2');
rightRect = drawRect('.right.svg-example-2');


filter = new SvgFilter();

filter
  .append('turbulence')
      .attr('type', 'turbulence')
      .attr('baseFrequency', 0.03)
      .attr('numOctaves', 5)
      .attr('width', 100)
      .attr('height', 100)
      .attr('x', 50)
      .attr('y', 50);

rightRect.attr('filter', filter);

It is often useful to combine the output of two filter effects. In the next example I show how to combine the blur and noise effects by using "composite":

Compositing Effects

Without Filter

With Filter

See examples of different compositing effects here.

var SvgFilter = require('svg-filter');
var drawRect = require('../shortcuts/draw-rect');

leftRect = drawRect('.left.svg-example-3');
rightRect = drawRect('.right.svg-example-3');


filter = new SvgFilter();

var blurComponent = filter
                      .append('blur')
                          .attr('stdDeviation', 5);

var noiseComponent = filter
                      .append('turbulence')
                          .attr('type', 'turbulence')
                          .attr('baseFrequency', 0.03)
                          .attr('numOctaves', 5)
                          .attr('width', 100)
                          .attr('height', 100)
                          .attr('x', 50)
                          .attr('y', 50);


// composite the two effects
filter
  .composite(blurComponent, noiseComponent)
      .attr('operator', 'xor');

rightRect
    .attr('filter', filter);

Flood, Offset, & Merge

Without Filter

With Filter

var SvgFilter = require('svg-filter');
var d3 = require('d3');

var makeText = function (selector) {
  return d3.select(selector)
            .append('svg')
                .attr('width', 200)
                .attr('height', 130)
              .append('text')
                  .text('text')
                  .attr('x', 100)
                  .attr('y', 110)
                  .style('font-size', 72 + 'px')
                  .style('font-weight', 'bold')
                  .style('font-family', 'Work Sans')
                  .attr('text-anchor', 'middle');
};

var leftText = makeText('.left.svg-example-4');
var rightText = makeText('.right.svg-example-4');

var filter = new SvgFilter();
var makeColoredFilter = function(color, dx, dy) {
  return filter
          .append('flood')
              .attr('color', color)
              .attr('opacity', 0.5)
          // the `to` command creates a
          // new 'composite' effect and
          // automatically sends the output
          // of the flood effect to the input
          // of composite.
          .to('composite')
              .attr('operator', 'in')
              .in2('SourceAlpha') // shorthand for attr('in2', 'SourceAlpha')

          // again, this automatically wires the
          // output of composite to the input of a new
          // 'offset' effect
          .to('offset')
              .attr('dx', dx)
              .attr('dy', dy);
};

var red = makeColoredFilter('#ff0000', 0, -12);
var green = makeColoredFilter('#00ff00', 0, -6);
var yellow = makeColoredFilter('#ffff00', 0, 0);
var blue = makeColoredFilter('#0000ff', 0, 6);

filter
  .merge(red, green, yellow, blue);

rightText
    .attr('filter', filter);

Convolution Matrix

Without Filter

With Filter

var d3 = require('d3');
var SvgFilter = require('svg-filter');

var imW = 1456 / 8;
var imH = 1955 / 8;

var drawImage = function (selector) {
  return d3.select(selector)
            .append('svg')
                .attr('width', imW)
                .attr('height', imH)
              .append('image')
                  .attr('width', imW)
                  .attr('height', imH)
                  .attr('xlink:href', './images/llama.jpg');
}

var leftImage = drawImage('.left.svg-example-5');
var rightImage = drawImage('.right.svg-example-5')

var filter = new SvgFilter();

filter
  .append('convolve')
      .attr('kernelMatrix', [0, 1, 0, 1, -4, 1, 0, 1, 0])
      .attr('preserveAlpha', true)
      .attr('bias', 0);


rightImage.attr('filter', filter);

Drop Shadow

Without Filter

With Filter

Example adapted from https://docs.webplatform.org/wiki/svg/tutorials/smarter_svg_filters.


var d3 = require('d3');
var SvgFilter = require('svg-filter');

var imW = 900 / 8;
var imH = 900 / 8;

var drawTiger = function (selector) {
  var margin = 20;
  return d3.select(selector)
            .append('svg')
                .attr('width', imW + margin)
                .attr('height', imH + margin)
              .append('image')
                  .attr('width', imW)
                  .attr('height', imH)
                  .attr('x', margin / 2)
                  .attr('y', margin / 2)
                  .attr('xlink:href', 'https://upload.wikimedia.org/wikipedia/commons/f/fd/Ghostscript_Tiger.svg');
}

var leftImage = drawTiger('.left.svg-example-6');
var rightImage = drawTiger('.right.svg-example-6')

var filter = new SvgFilter();



// Take the alpha channel of the original image,
// blur it, then offset it slightly
var blur = filter
            .append('blur')
                .in('SourceAlpha')
                .attr('stdDeviation', 2)
            // send to output of the blur
            // to the input of the offset
            .to('offset')
                .attr('dx', 4)
                .attr('dy', 6);

// Create a swatch of gray color
var flood = filter
              .append('flood')
                  .attr('color', '#777');


// Combine the blurred offset with the color we want. This
// is the 'drop shadow'
var composite = filter.composite(blur, flood).attr('operator', 'in');

// Combine the drop shadow with the
// original image
filter.merge(composite, 'SourceGraphic');


rightImage.attr('filter', filter);