Search code examples
javascriptcssd3.jssvgradial-gradients

D3 colour gradient not forming mathematical circle around centre of area


I am trying to do a radial area plot with a colour gradient to reflect this. However the gradient does not appear to ever want to sit perfectly in line / at the correct side with the circle (see image below).

I have tried constraining the width and hight of the svg so that they are equal, but with no avail.Every refresh (with random data), the central ring for the gradient will shift and warp into a different shape, but never lie on the mean line where it should be.

enter image description here

I have ensured that the svg is square

 var width = window.innerWidth ,   height = window.innerHeight;

  width = height =   d3.min([width,height])

  var data = d3.range(0,100).map(d=>Math.random())

Given the gradient the following properties

 var linearGradient = defs.append("radialGradient")
.attr("id", "rad-gradient")
.attr("cx", "50%")    //The x-center of the gradient, same as a typical SVG circle
.attr("cy", "50%")    //The y-center of the gradient
.style("r", "50%");

and my area mathematically as

var area = d3.area()
 .x1(function(d,i) { return width/2+Math.cos(i\*angle)*(h(d)) })     .x0(function(d,i) { return > width/2+Math.cos(i\*angle)*(h(mean)) })    .y0(function(d,i) {> return height/2+Math.sin(i\*angle)*(h(mean)) })    .y1(function(d,i) { return height/2+Math.sin(i\*angle)*(h(d)) })

And appended the created gradient to the svg path

svg.append("path")
    .data([data])
    .attr("class", "area")
    .attr("d", area)
    .style("fill", "url(#rad-gradient)")

Solution

  • The solution lies in creating a circle and filling it with the correct gradient. This ensures that the radial gradient will always be centered around the origin. enter image description here

    You can then use clipPath to extract only the path you wish to use from the circle. After a little fiddling to align the gradient radius, we get the desired effect.

    // previous definition for area path appended as a clip path
    clip = defs.append("clipPath")
      .attr('id','clip')
      .append("path")
      .data([data])
      .attr("class", "area")
      .attr("d", area)
    
    //extracting this path from a circle with the desired gradient
    svg.append("circle")
    
      .attr("cx", width/2)
      .attr("cy", height/2)
            .attr("r", rmax)
            .attr("clip-path","url(#clip)")
          .style("fill", "url(#linear-gradient)")
    

    clipped path from circle