I’m trying to create doughnut visualization using d3 with fadeout/gradient effect for each arc like this:
I’ve tried to add ‘filter: drop-shadow(0 0 10px color)’ but it doesn’t give me this effect, also, it spread on both sides.
So I moved to use two pies
- Outer solid color
- Inner with (hopefully) the fade out effect
I know in gradient I can make solid, then fade out but since I don’t have a clear path to success, I split to two pies
I’ve tried to use both linearGradient & radialGradient on the second pie which seems like a step in a good direction, but the gradient effect changes as the arcs angles are different:
For the simplicity, currently I created a gradient filter for just one color as I’m not sure this is the solution for my problem…
I’ve added a snippet to help the helpers 🙂
const colors = ['red','green','purple','blue','orange','teal']
const data = [10,12,33,112,22,4];
const pie = d3.pie().padAngle(0.03);
const arcs = pie(data);
const svg = d3.select('body')
.append('svg');
const svgDefs = svg.append('defs');
const mainGradient = svgDefs.append('radialGradient')
.attr('id', 'mainGradient');
mainGradient.append('stop')
.attr('stop-color', 'red')
.attr('offset', '10%');
mainGradient.append('stop')
.attr('stop-color', 'pink')
.attr('offset', '90%');
d3.select('body')
.style('position', 'relative')
.append('svg')
.style('position', 'absolute')
.style('z-index', 10)
.attr('width', 600)
.attr('height', 600)
.append("g")
.attr("transform", "translate(100,100)")
.selectAll('path')
.data(arcs)
.enter()
.append('path')
.attr('d', (a,b,c) => d3.arc()({...a, innerRadius: 96, outerRadius: 100}))
.style('fill', (d, index) => colors[index])
svg.style('position', 'absolute')
.attr('width', 600)
.attr('height', 600)
.append("g")
.attr("transform", "translate(100,100)")
.selectAll('path')
.data(arcs)
.enter()
.append('path')
.transition()
.attr('d', (a,b,c) => d3.arc()({...a, innerRadius: 0, outerRadius: 96}))
.style('fill', 'url(#mainGradient)')
<html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.14.2/d3.min.js"></script>
<body>
</body>
</html>
2
Answers
I’m not familiar with the D3 library. However, I can answer your question from a native SVG perpective, and then hopefully you can use those principles to create what you need in D3.
A radial gradient is the correct solution, but you will probably need to define a different gradient for each slice of your doughnut. You also need to specify the position of each gradient as being the centre of your doughnut. (If you don’t specify a position for a radial gradient, it will position itself in the approximate centre of the fill area of the path to which it is applied, which in this case is not what you want.)
Also, you will need to use two paths to create each slice:
stroke
to create the bright edge of the slice. We can’t fill this path, though, because the fill would only include the area bounded by a direct line between the two endpoints of the path.fill
, which follows the same arc as the first path, but then extends to the centre of the doughnut and back to the start of the path (making a sector or “pizza slice”).This is similar to the idea stated in your question for having one pie for the outer edge and a second pie for the gradient fill.
Each circle has a specific color and stroke-dasharray that controls the length. On top of all the circles there is a mask and the mask has a gradient to control the fill.