Search code examples
javascriptd3.jsdonut-chart

d3 donut chart with draggable outerRadius


I'm trying to make a donut chart with a changeable radius at every wedge. Every Wedge should be draggable to change the breadth of this wedge (breadth means the outerRadius). But I don't know how to implement this drag function. Can someone help me?

Here is my code:

var width = 760,
    height = 550;

var innerradius = 200;

var color = d3.scale.category20b();     

var cScale = d3.scale.linear().domain([0, 100]).range([0, 2 * Math.PI]);



var dataset = [
      { label: 'Abulia', count: 10, start: 0, end: 10, radius: 10 }, 
      { label: 'Betelgeuse', count: 20, start: 10, end: 20, radius: 20 },
      { label: 'Cantaloupe', count: 30, start: 30, end: 60, radius: 20 },
      { label: 'Dijkstra', count: 40, start: 60, end: 100, radius: 20 }
];



var svg = d3.select('#content').append('svg')
            .attr('width', width)
            .attr('height', height)
            .append('g')
            .attr('transform', 'translate(' + (width / 2) +  ',' + (height / 2) + ')');


var arc = d3.svg.arc()
      .innerRadius(innerradius)             
      .outerRadius(function(d){return d.radius + innerradius;})
      .startAngle(function(d){return cScale(d.start);}) 
      .endAngle(function(d){return cScale(d.end);});

var path = svg.selectAll('path')
      .data(dataset)
      .enter()
      .append('path')
      .attr('d', arc)
      .style("fill", function(d){return color(d.label);});   

var drag = d3.behavior.drag()
             .on('drag', function() {

                    //don't know what to do

                                    }) 

Solution

  • Here's my solution.

                 .on('drag', function(d) {
                 if (d3.event.sourceEvent.button == 0)
                     d.radius += Math.sqrt(d3.event.dx * d3.event.dx + d3.event.dy * d3.event.dy);
                 else if (d3.event.sourceEvent.button == 2){
                     d.radius -= Math.sqrt(d3.event.dx * d3.event.dx + d3.event.dy * d3.event.dy);
                     d.radius = Math.max (d.radius, 1);
                 }
                    path.each(function (d2){
                        if (d == d2){
                            d3.select(this).attr('d', arc);
                        }
                    });
                                    });
    

    With this, left clicking and dragging will increase the radius of the wedge while right click and drag will decrease it. If you choose to keep this behavior then you'd also want to override the default context menu to prevent it from popping up.

    However, if you want to be able to both expand and contract with left click, things get a tad more complicated. This is because this method relies on the dx and dy properties of the drag event, which will be the same direction regardless of where the arc is. To use it with a single mouse button you'd have to calculate dx and dy relative to the direction that the arc is facing (probably need to delve into a bit of trigonometry for this).