Search code examples
svgd3.jsgradientradial-gradients

Gradient colors from d3.scale.category10() with opacity change on a svg circle?


I am trying to apply the colors from the color = d3.scale.category10(); var to the gradient for the circle svg, what am I doing wrong? All I am seeing is the first color of the color = d3.scale.category10();(which is blue) to 0% opacity gradient but that is all. If I take the gradient out then I see the range I want which is from 1-4? Thanks in advance!

var nodes = d3.range(300).map(function() { return {radius: Math.random() * 12 + 4}; }),
    root = nodes[0],
    color = d3.scale.category10();

root.radius = 0;
root.fixed = true;

var force = d3.layout.force()
  .gravity(0.05)
  .charge(function(d, i) { return i ? 0 : -4000; })
  .nodes(nodes)
  .size([width, height]);

  force.start();

var svg = d3.select("body").append("svg:svg")
  .attr("width", width)
  .attr("height", height);

var gradient = svg.append("defs").append("radialGradient")
  .attr("id", "gradient")
  .attr("cx", "50%")
  .attr("cy", "50%");

gradient.append("stop")
  .attr("offset", "75%")
  .style("stop-color", function(d, i) { return color(i % 4); })
  .attr("stop-opacity", "1");

gradient.append("stop")
  .attr("offset", "100%")
  .style("stop-color", function(d, i) { return color(i % 4); })
  .attr("stop-opacity", ".1");

svg.selectAll("circle")
  .data(nodes.slice(1))
  .enter().append("circle")
  .attr("r", function(d) { return d.radius; })
  .style("fill", "url(#gradient)");

Solution

  • Your stop elements don't have any data joined with them, so in your function (d, i), i will always be 0. If you just want the two stops, you could do something like this:

    gradient.append("stop")
      .attr("offset", "75%")
      .style("stop-color", color(0))
      .attr("stop-opacity", "1");
    
    gradient.append("stop")
      .attr("offset", "100%")
      .style("stop-color", color(1))
      .attr("stop-opacity", ".1");
    

    If instead you're just trying to fade the edges of your circles, a gradient isn't what you want at all. Instead, you'll need to apply a solid color to each circle, then create a single opacity-only gradient inside a mask, and apply that mask to each circle. Something like this:

    var defs = svg.append('defs');
    var gradient = defs.append('radialGradient')
        .attr('id', 'fadient');
    gradient.append('stop')
        .attr('offset', '75%')
        .attr('stop-color', 'white')
        .attr('stop-opacity', 1)
    gradient.append('stop')
        .attr('offset', '100%')
        .attr('stop-color', 'white')
        .attr('stop-opacity', .1)
    
    var mask = defs.append('mask')
        .attr('id', 'mask')
        .attr('maskContentUnits', 'objectBoundingBox')
      .append('circle')
        .attr('fill', 'url(#fadient)')
        .attr('cx', .5)
        .attr('cy', .5)
        .attr('r', .5)
    
    svg.selectAll("circle")
      .data(nodes.slice(1))
      .enter().append("circle")
      .attr('cx', function (d, i) { return 20 * i })
      .attr('cy', 50)
      .attr("r", function(d) { return d.radius; })
      .attr('mask', 'url(#mask)')
      .attr("fill", function (d, i) { return color(i); });