title: D3 transition: Fading in and out the colors within a gradient fill
body: <p>In this D3 diagram, the circles are filled with radial gradients, and changing opacity is used for fading in and fading out:</p>
  
  <p><img src="https://i.stack.imgur.com/xZ2GF.gif" alt="Animated Screenshot of desired effect"></p>
  
  <p><div class="snippet" data-lang="js" data-hide="true" data-console="false" data-babel="false">
  <div class="snippet-code snippet-currently-hidden">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>var width = 400,
      height = 400,
      padding = 1.5, // separation between same-color nodes
      clusterPadding = 6, // separation between different-color nodes
      maxRadius = 12;
  
  var n = 200, // total number of nodes
      m = 10; // number of distinct clusters
  
  var color = d3.scale.category10()
      .domain(d3.range(m));
  
  // The largest node for each cluster.
  var clusters = new Array(m);
  
  var nodes = d3.range(n).map(function() {
      var i = Math.floor(Math.random() * m),
          r = Math.sqrt((i + 1) / m * -Math.log(Math.random())) * maxRadius,
          d = {cluster: i, radius: r};
      if (!clusters[i] || (r &gt; clusters[i].radius)) clusters[i] = d;
      return d;
  });
  
  // Use the pack layout to initialize node positions.
  d3.layout.pack()
      .sort(null)
      .size([width, height])
      .children(function(d) { return d.values; })
      .value(function(d) { return d.radius * d.radius; })
      .nodes({values: d3.nest()
          .key(function(d) { return d.cluster; })
          .entries(nodes)
      });
  
  var force = d3.layout.force()
      .nodes(nodes)
      .size([width, height])
      .gravity(.02)
      .charge(0)
      .on("tick", tick)
      .start();
  
  var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height);
  
  var grads = svg.append("defs").selectAll("radialGradient")
      .data(nodes)
     .enter()
      .append("radialGradient")
      .attr("gradientUnits", "objectBoundingBox")
      .attr("cx", 0)
      .attr("cy", 0)
      .attr("r", "100%")
      .attr("id", function(d, i) { return "grad" + i; });
  
  grads.append("stop")
      .attr("offset", "0%")
      .style("stop-color", "white");
  
  grads.append("stop")
      .attr("offset", "100%")
      .style("stop-color",  function(d) { return color(d.cluster); });
  
  var node = svg.selectAll("circle")
      .data(nodes)
     .enter()
      .append("circle")
      .style("fill", function(d, i) {
          return "url(#grad" + i + ")";
      })
      // .style("fill", function(d) { return color(d.cluster); })
      .call(force.drag)
      .on("mouseover", fade(.1))
      .on("mouseout", fade(1));
  
  node.transition()
      .duration(750)
      .delay(function(d, i) { return i * 5; })
      .attrTween("r", function(d) {
        var i = d3.interpolate(0, d.radius);
        return function(t) { return d.radius = i(t); };
      });
  
  
  function fade(opacity) {
      return function(d) {
          node.transition().duration(1000)
              .style("fill-opacity", function(o) {
                  return isSameCluster(d, o) ? 1 : opacity;
              })
              .style("stroke-opacity", function(o) {
                  return isSameCluster(d, o) ? 1 : opacity;
              });
      };
  };
  
  function isSameCluster(a, b) {
       return a.cluster == b.cluster;
  };
  
  
  function tick(e) {
      node
          .each(cluster(10 * e.alpha * e.alpha))
          .each(collide(.5))
          .attr("cx", function(d) { return d.x; })
          .attr("cy", function(d) { return d.y; });
  }
  
  // Move d to be adjacent to the cluster node.
  function cluster(alpha) {
      return function(d) {
          var cluster = clusters[d.cluster];
          if (cluster === d) return;
          var x = d.x - cluster.x,
              y = d.y - cluster.y,
              l = Math.sqrt(x * x + y * y),
              r = d.radius + cluster.radius;
          if (l != r) {
              l = (l - r) / l * alpha;
              d.x -= x *= l;
              d.y -= y *= l;
              cluster.x += x;
              cluster.y += y;
          }
      };
  }
  
  // Resolves collisions between d and all other circles.
  function collide(alpha) {
      var quadtree = d3.geom.quadtree(nodes);
      return function(d) {
          var r = d.radius + maxRadius + Math.max(padding, clusterPadding),
              nx1 = d.x - r,
              nx2 = d.x + r,
              ny1 = d.y - r,
              ny2 = d.y + r;
          quadtree.visit(function(quad, x1, y1, x2, y2) {
              if (quad.point &amp;&amp; (quad.point !== d)) {
                  var x = d.x - quad.point.x,
                      y = d.y - quad.point.y,
                      l = Math.sqrt(x * x + y * y),
                      r = d.radius + quad.point.radius +
                         (d.cluster === quad.point.cluster ? padding : clusterPadding);
                  if (l &lt; r) {
                      l = (l - r) / l * alpha;
                      d.x -= x *= l;
                      d.y -= y *= l;
                      quad.point.x += x;
                      quad.point.y += y;
                  }
              }
              return x1 &gt; nx2 || x2 &lt; nx1 || y1 &gt; ny2 || y2 &lt; ny1;
          });
      };
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"&gt;&lt;/script&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <p>(<a href="http://jsfiddle.net/mwceatqr/" rel="nofollow noreferrer">Same code as a jsfiddle</a>)</p>
  
  <p>How to use color for fading in and fading out, instead of opacity? For example, let say we want to make all circles gray while in "faded out" state", and bring them back to their original color in their "normal state"?  You can't just transition the <code>fill</code> property as a color value, because the <code>fill</code> is a URL reference to a <code>&lt;radialGradient&gt;</code> element.</p>