Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 Gradient Fill on Bar Chart - with colour scale

Tags:

d3.js

I have managed to apply a gradient to my bar chart and the gradient effect gives the desired result.

var gradient = svg.append("defs")
  .data(data)
  .append("linearGradient")
    .attr("id", "gradient")
    .attr("x1", "0%")
    .attr("y1", "0%")
    .attr("x2", "100%")
    .attr("y2", "00%")
    .attr("spreadMethod", "pad");

gradient.append("stop")
    .attr("offset", "0%")
    .attr("stop-color", function(d) {
    return colors(d.name);
  })
    .attr("stop-opacity", 1);

gradient.append("stop")
    .attr("offset", "100%")
    .attr("stop-color", function(d) {
    return colors(d.name);
  })
    .attr("stop-opacity", 0.3);

I also have a color scale that I'm using to assign colors to each category (name)

var colors = d3.scale.ordinal()
  .range(["#C1D42F", "#2b328c", "#5AB88D", "#8F1F61", "#00A5D3", "#EC5D20", "#F59C28"])

This was working fine before I started to use gradient

However, now it appears that the function I'm using is only taking the first color (#C1D42F) from my range.

How can I apply the gradient as well as assign the ranged colors ?

Here is my fiddle

like image 509
Gangrel Avatar asked Jan 21 '26 17:01

Gangrel


1 Answers

There are two issues related to this result

1) The gradient definition is missing the .enter() method

This is necessary to create a linearGradient element for each data point. Instead of:

var gradient = svg.append("defs")
  .data(data)
  .append("linearGradient")
    .attr("id", "gradient")
    .attr("x1", "0%")
    .attr("y1", "0%")
    .attr("x2", "100%")
    .attr("y2", "00%")
    .attr("spreadMethod", "pad");

You could use:

var gradient = svg.append("defs")
  .selectAll("linearGradient") // Creates the initial selection of linear gradients
  .data(data)
  .enter() // Binds new linearGradient elements for each data point
  .append("linearGradient")
    .attr("id", "gradient")
    .attr("x1", "0%")
    .attr("y1", "0%")
    .attr("x2", "100%")
    .attr("y2", "00%")
    .attr("spreadMethod", "pad");

Now, instead of having only one linearGradient element, you have one for each color. However, you will notice the problem persists, which leads to the second issue:

2) If all the linear gradients have the same ID, the code can not differentiate between colors.

Different linearGradient elements need different IDs in order to reference the data they represent. Continuing the previous example, instead of:

var gradient = svg.append("defs")
  .selectAll("linearGradient") // Creates the initial selection of linear gradients
  .data(data)
  .enter() // Binds new linearGradient elements for each data point
  .append("linearGradient")
    .attr("id", "gradient")
    .attr("x1", "0%")
    .attr("y1", "0%")
    .attr("x2", "100%")
    .attr("y2", "00%")
    .attr("spreadMethod", "pad");

You could use:

var gradient = svg.append("defs")
  .selectAll("linearGradient") // Creates the initial selection of linear gradients
  .data(data)
  .enter() // Binds new linearGradient elements for each data point
  .append("linearGradient")
    .attr("id", d => `gradient${d.name}`) // Create a unique data-driven id for each linearGradient
    .attr("x1", "0%")
    .attr("y1", "0%")
    .attr("x2", "100%")
    .attr("y2", "00%")
    .attr("spreadMethod", "pad");

And in the bars, the code can now reference the correct linearGradient according to the data:


bars.append("rect")
...
.style("fill", d => `url(#gradient${d.name})`); // picks the gradient that match the data

like image 122
Rodrigo Divino Avatar answered Jan 23 '26 06:01

Rodrigo Divino