Add border-radius property to D3js Donut Chart
Asked Answered
L

2

5

I have this donut chart currently working in an AngularJS app:

enter image description here

But the design mockup says we would like this, note the border-radius property on the green portion of the arc:

enter image description here

How do I add a border-radius to the SVG that d3js outputs, the code I'm currently using looks like this:

let data = [
    {
        label: 'Data',
        count: scope.data
    },
    {
        label: 'Fill',
        count: 100 - scope.data
    }
];

let width = 60;
let height = 60;
let radius = Math.min(width, height) / 2;
let color = d3.scale
    .ordinal()
    .range(['#3CC692', '#F3F3F4']);
let selector = '#donut-asset-' + scope.chartId;

d3
    .select(selector)
    .selectAll('*')
    .remove();

let svg = d3
    .selectAll(selector)
    .append('svg')
    .attr('width', width)
    .attr('height', height)
    .append('g')
    .attr(
        'transform',
        'translate(' + width / 2 + ',' + height / 2 + ')'
    );
let arc = d3.svg
    .arc()
    .innerRadius(23)
    .outerRadius(radius);
let pie = d3.layout
    .pie()
    .value(function(d) {
        return d.count;
    })
    .sort(null);
let path = svg
    .selectAll('path')
    .data(pie(data))
    .enter()
    .append('path')
    .attr('d', arc)
    .attr('fill', function(d, i) {
        return color(d.data.label);
    });
let legend = svg
    .selectAll('.legend')
    .data(data)
    .enter()
    .append('g')
    .attr('class', 'legend')
    .attr('transform', function(d, i) {
        return 'translate(' + 0 + ',' + 0 + ')';
    });
legend
    .append('text')
    .attr('x', 1)
    .attr('y', 1)
    .attr('text-anchor', 'middle')
    .attr('dominant-baseline', 'central')
    .text(function(d) {
        return d.count + '%';
    });

};

I know to use cornerRadius but when I do it sets a radius for both arcs, it just needs to exist on the colored one. Any ideas? Thanks in advance for any help!

Lathan answered 2/5, 2018 at 23:19 Comment(0)
Z
11

You can apply a corner radius to a d3 arc which allows rounding on the corners:

let arc = d3.svg
    .arc()
    .innerRadius(23)
    .outerRadius(radius)
    .cornerRadius(10);

But, the downside is that all arcs' borders are rounded:

enter image description here

If you apply the cornerRadius to only the darkened arc - the other arc won't fill in the background behind the rounded corners. Instead, we could append a circular arc (full donut) and place the darkened arc on top with rounding (my example doesn't adapt your code, just shows how that it can be done, also with d3v4 which uses d3.arc() rather than d3.svg.arc() ):

var backgroundArc = d3.arc()
  .innerRadius(30)
  .outerRadius(50)
  .startAngle(0)
  .endAngle(Math.PI*2);
  
var mainArc = d3.arc()
  .innerRadius(30)
  .outerRadius(50)
  .cornerRadius(10)
  .startAngle(0)
  .endAngle(function(d) { return d/100*Math.PI* 2 });
  
var data = [10,20,30,40,50] // percents.
  
var svg = d3.select("body").append("svg")
  .attr("width", 600)
  .attr("height", 200);
  
var charts = svg.selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .attr("transform",function(d,i) { 
     return "translate("+(i*100+50)+",100)";
  });
  
charts.append("path")
  .attr("d", backgroundArc)
  .attr("fill","#ccc")

charts.append("path")
  .attr("d", mainArc) 
  .attr("fill","orange")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
Zagazig answered 2/5, 2018 at 23:58 Comment(2)
Ok gotcha, so I have to separate the arcs and only give the colored one the cornerRadius.Lathan
This will be probably the most straight forward - otherwise you need to modify the start and end angles of the grey arc so that it can cover the rounded portions, plus then there might be layering issues in a pie layout.Zagazig
L
0

Try playing with stroke attributes like:

  • stroke
  • stroke-dasharray
  • stroke-dashoffset
  • stroke-linecap
  • stroke-linejoin
  • stroke-miterlimit
  • stroke-opacity
  • stroke-width

And set width of bar to lower values, or 0.

Reference: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute

But the better way is to make charts on canvas, because you can draw everything you want. Or to use an library.

Limemann answered 2/5, 2018 at 23:35 Comment(4)
I am using a library, it's d3js.Lathan
And this is wrong choise to achieve your goal. Try another. Or try doing it manual, it is easy. But first try my sugestion with stroke.Limemann
I don't think you can set a stroke on a close path like thisEffluvium
Before saying firstly checkLimemann

© 2022 - 2024 — McMap. All rights reserved.