MattDionis
MattDionis

Reputation: 3616

D3: Set filter flood-color based on data

I'm attempting to set the filter flood-color within a D3 visualization based on a property in my data. Currently my filter setup looks like so, and works as expected, setting a red flood-color:

var defs = vis.append('defs');

    var filter = defs.append('filter')
      .attr('id', 'drop-shadow')
      .attr('height', '130%');

    filter.append('feGaussianBlur')
      .attr('in', 'SourceAlpha')
      .attr('stdDeviation', 3)
      .attr('result', 'blur');

    filter.append('feOffset')
      .attr('in', 'blur')
      .attr('result', 'offsetBlur');

    filter.append("feFlood")
      .attr("in", "offsetBlur")
      .attr("flood-color",'red')
      .attr("flood-opacity", "1")
      .attr("result", "offsetColor");

    filter.append("feComposite")
      .attr("in", "offsetColor")
      .attr("in2", "offsetBlur")
      .attr("operator", "in")
      .attr("result", "offsetBlur");

    var feMerge = filter.append('feMerge');

    feMerge.append('feMergeNode')
      .attr('in', 'offsetBlur')
    feMerge.append('feMergeNode')
      .attr('in', 'SourceGraphic');

However, if I change this section to try to set the color based on my data, the data is not defined:

filter.append("feFlood")
  .attr("in", "offsetBlur")
  .attr("flood-color", function(d) {
    if (d.state === 'active') {
      return 'blue';
    } else if (d.state === 'starter') {
      return 'green';
    } else if (d.state === 'suspended') {
      return 'yellow';
    } else if (d.state === 'closed') {
      return 'red';
    }
  })
  .attr("flood-opacity", "1")
  .attr("result", "offsetColor");

UPDATE: Link to full example: http://blockbuilder.org/MattDionis/7f5375d927698f508a01

Upvotes: 2

Views: 1459

Answers (1)

Cyril Cherian
Cyril Cherian

Reputation: 32327

I don't think you should approach the problem by changing the filter color. Filter is commonly used by all nodes so changing the filter color will change in all the nodes.

I would suggest that for each category make a new filter example:

if (d.state === 'active') {
      return 'blue';//make blue filter
    } else if (d.state === 'starter') {
      return 'green';//make green filter
    } else if (d.state === 'suspended') {
      return 'yellow';//make yellow filter
    } else if (d.state === 'closed') {
      return 'red';////make red filter
    }

I have made a function which will make the filter inside defs if not present:

function makeFiter(id, color){
//make a filter if filter id not present
if (defs.selectAll("#"+id).empty()){
  var filter = defs.append('filter')
    .attr('id', id)
    .attr('height', '130%');

  filter.append('feGaussianBlur')
    .attr('in', 'SourceAlpha')
    .attr('stdDeviation', 3)
    .attr('result', 'blur');

  filter.append('feOffset')
    .attr('in', 'blur')
    .attr('result', 'offsetBlur');

  filter.append("feFlood")
    .attr("in", "offsetBlur")
    .attr("flood-color",color)
    .attr("flood-opacity", "1")
    .attr("result", "offsetColor");

  filter.append("feComposite")
    .attr("in", "offsetColor")
    .attr("in2", "offsetBlur")
    .attr("operator", "in")
    .attr("result", "offsetBlur");

  var feMerge = filter.append('feMerge');

  feMerge.append('feMergeNode')
    .attr('in', 'offsetBlur')
  feMerge.append('feMergeNode')
    .attr('in', 'SourceGraphic');  
}
return 'url(#'+id+")";//return the filter id
}

I have made another function to pull the correct filter url depending on the condition.

  function getFilter(d){
    if (d.state === 'active') {
      return makeFiter('drop-shadow-blue','blue');
    } else if (d.state === 'starter') {
      return makeFiter('drop-shadow-green','green');
    } else if (d.state === 'suspended') {
      return makeFiter('drop-shadow-yellow','yellow');
    } else if (d.state === 'closed') {
      return makeFiter('drop-shadow-red','red');
    }
  }

Now when you create the node call the getFilter method to return the correct filter id based on the condition defined above.

nodeEnter = node.enter().append('svg:g')
          .attr('class', 'node')
          .attr('transform', function(d) {
            return "translate(" + d.x + "," + d.y + ")";
          })
          .attr('filter', function(d){return getFilter(d);})//get the filter url

So in the DOM your defs will look something like this with ids:

  • drop-shadow-red
  • drop-shadow-blue
  • drop-shadow-green
  • drop-shadow-yellow

Working code here.

Upvotes: 3

Related Questions