D3.js gradients — trickier than they look

Bryony Miles
2 min readNov 24, 2016

--

The brief was to plot start and end values on an x/y axis with a gradient rectangle in between moving from low (opacity 0.4) to high (opacity 0).

I soon discovered that gradients were not a built in feature of d3.js.

For my visual, each rectangle had to have a unique gradient to display the correct color in the right direction. To solve this I wrote a gradients function taking several variables as follows:

Step 1 was to append the ‘def’ and the gradient to the svg:

svg.append(“defs”)
.append(“linearGradient”)

Step 2 is to define the unique gradient id (variable 1):

.attr(“id”,id)

Next, define the direction of the gradient using variable 2 and 3: x1, x2. For left to right x1 = 0%, x2 = 100% and vice versa.

.attr(“x1”, x1).attr(“y1”, “0%”)
.attr(“x2”, x2).attr(“y2”, “0%”);

Next, select the new def twice using the defined idtag:

idtag = ‘#’+id

and define a begin class and an end class. Both these selections take our final five variables: colour,off1,off2, op1 and op2.

d3.select(idtag)
.append(“stop”)
.attr(“stop-color”, colour)
.attr(“class”,”begin”)
.attr(“offset”, off1)
.attr(“stop-opacity”, op1);


d3.select(idtag)
.append(“stop”)
.attr(“class”,”end”)
.attr(“stop-color”, colour)
.attr(“offset”, off2)
.attr(“stop-opacity”, op2);

The offset was used for the circular end points which are actually circles with gradient. So if the change is negative (high to low, orange,red and purple on the picture), begin offset is 50%, end offset is 0%, i.e only the right hand half of the circle is drawn.

The opacity works in a similar way. So if the change is positive (low to high, green and blue on the picture), the begin opacity is 0.4 (0.4 opacity) and end opacity is 1 (full color).

The function then needs to be called when setting the fill style in your rectangle or your circle (or any other shape for that matter):

.style(“fill”,function (d) {
if(d.end_v>d.start_v){
gradient(color(d.label),’circ’+ d.id,”100%”,”0%”,”50%”,”0%”,0,1)
}else{
gradient(color(d.label),’circ’+ d.id,”0%”,”100%”,”50%”,”0%”,0,1)

}return “url(#circ” + d.id +”)”; })

The returned tag “url(#circ” + d.id +”)” puts the gradient into action.

If you want to try it out, you can find the visualization in action here or the code in full here.

--

--