首页 > 解决方案 > 在 D3 Donut Chart 中为总计添加更细的弧

问题描述

我收到如下 JSON 响应:

var dataset = [
    {positive: 20, negative: 40 ,total: 100}
  ];

我想在类似于这样的圆环图中显示数据:

在此处输入图像描述

在这种情况下,积极的将是20/100消极 40/100的,其余的将total-negative-positive100-40-20 = 60

这非常有效,但我希望它在 D3 图表中:

Chart.types.Doughnut.extend({
    name: "DoughnutAlt",
    initialize: function (data) {
        Chart.types.Doughnut.prototype.initialize.apply(this, arguments);

        // save the actual clear method
        var originalClear = this.clear;
        // override the clear method to draw the background after each clear
        this.clear = function () {
            originalClear.apply(this, arguments)

            var ctx = this.chart.ctx;
            // use any one of the segments to get the inner and outer radius and center x and y
            var firstSegment = this.segments[0];
            // adjust 0.3 to increaase / decrease the width of the background
            var gap = (firstSegment.outerRadius - firstSegment.innerRadius) * (1 - 0.3) / 2;

            ctx.save();
            ctx.fillStyle = "#EEE";
            ctx.beginPath();
            ctx.arc(firstSegment.x, firstSegment.y, firstSegment.outerRadius - gap, 0, 2 * Math.PI);
            ctx.arc(firstSegment.x, firstSegment.y, firstSegment.innerRadius + gap, 0, 2 * Math.PI, true);
            ctx.closePath();
            ctx.fill();
            ctx.restore();
        }
    }
});


var pointsUsed = [
    {
        value: 20,
        color: "#c7003b",
    },
    {
        value: 40,
        color: "#000",
    },    
    {
        value: 60,
        color: "transparent",
    },
];

var pointsUsed_ctx = document.getElementById("pointsUsed").getContext("2d");

var pointsUsed = new Chart(pointsUsed_ctx).DoughnutAlt(pointsUsed, {
    segmentShowStroke: false,
    segmentStrokeWidth: 0,
    percentageInnerCutout: 87,
    showTooltips: false,
    animationEasing: 'easeInOutCubic',
    responsive: true
});
<script src="https://rawgit.com/nnnick/Chart.js/v1.0.2/Chart.min.js"></script>
<canvas id="pointsUsed" height="200" width="200"></canvas>

因此,我使用了一个响应式 d3 图表来呈现这一点,并且我已经调整了数据集以包含总数。我想要做的是使未使用部分的弧(总 - 正 - 负)具有更细的弧。

var dataset = {
    numbers: [20, 40, 40]
};

var width = 960,
  height = 500,
  radius = Math.min(width, height) / 2;

var enterClockwise = {
    startAngle: 0,
    endAngle: 0
};

var enterAntiClockwise = {
    startAngle: Math.PI * 2,
    endAngle: Math.PI * 2
};

//var color = d3.scale.category20();
var color = d3.scale.ordinal().range([d3.rgb("#c7003b"), d3.rgb('#000'), d3.rgb('#ccc'),d3.rgb('transparent')])

var pie = d3.layout.pie()
  .sort(null);

var arc = d3.svg.arc()
  .innerRadius(radius - 100)
  .outerRadius(radius - 20);

var svg = d3.select('#Donut-chart').append('svg')
     .attr('id', 'Donut-chart-render')
     .attr("width", '100%')
     .attr("height", '100%')
     .attr('viewBox', (-width / 2) + ' ' + (-height / 2) + ' ' + width + ' ' + height)
     .attr('preserveAspectRatio', 'xMinYMin')

var path = svg.selectAll("path")
  .data(pie(dataset.numbers))
  .enter().append("path")
    .attr("fill", function (d, i) { return color(i); })
    .attr("d", arc(enterClockwise))
    .each(function (d) {
        this._current = {
            data: d.data,
            value: d.value,
            startAngle: enterClockwise.startAngle,
            endAngle: enterClockwise.endAngle
        }
    });

path.transition()
    .duration(750)
    .attrTween("d", arcTween);



var timeout = setTimeout(function () {
    d3.select("input[value=\"oranges\"]").property("checked", true).each(change);
}, 2000);

function createChart() {
    clearTimeout(timeout);
    path = path.data(pie(dataset[this.value]));
    path.enter().append("path")
        .attr("fill", function (d, i) {
            return color(i);
        })
        .attr("d", arc(enterAntiClockwise))
        .each(function (d) {
            this._current = {
                data: d.data,
                value: d.value,
                startAngle: enterAntiClockwise.startAngle,
                endAngle: enterAntiClockwise.endAngle
            };
        }); // store the initial values

    path.exit()
        .transition()
        .duration(750)
        .attrTween('d', arcTweenOut)
        .remove() // now remove the exiting arcs

    path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}

function arcTween(a) {
    var i = d3.interpolate(this._current, a);
    this._current = i(0);
    return function (t) {
        return arc(i(t));
    };
}
function arcTweenOut(a) {
    var i = d3.interpolate(this._current, { startAngle: Math.PI * 2, endAngle: Math.PI * 2, value: 0 });
    this._current = i(0);
    return function (t) {
        return arc(i(t));
    };
}


function type(d) {
    d.value = +d.value;
    return d;
}

createChart(dataset);
body {
        margin: auto;
        position: relative;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.min.js"></script>
<div id="Donut-chart"></div>

标签: javascriptd3.js

解决方案


只需设置第二个电弧发生器,即可创建更细的线条。在最后一个数据点上使用它:

var dataset = {
    numbers: [20, 40, 40]
};

var width = 960,
  height = 500,
  radius = Math.min(width, height) / 2;

var enterClockwise = {
    startAngle: 0,
    endAngle: 0
};

var enterAntiClockwise = {
    startAngle: Math.PI * 2,
    endAngle: Math.PI * 2
};

//var color = d3.scale.category20();
var color = d3.scale.ordinal().range([d3.rgb("#c7003b"), d3.rgb('#000'), d3.rgb('#ccc'),d3.rgb('transparent')])

var pie = d3.layout.pie()
  .sort(null);

var arc = d3.svg.arc()
  .innerRadius(radius - 100)
  .outerRadius(radius - 20);
  
var arcThin = d3.svg.arc()
  .innerRadius(radius - 80)
  .outerRadius(radius - 40);

var svg = d3.select('#Donut-chart').append('svg')
     .attr('id', 'Donut-chart-render')
     .attr("width", '100%')
     .attr("height", '100%')
     .attr('viewBox', (-width / 2) + ' ' + (-height / 2) + ' ' + width + ' ' + height)
     .attr('preserveAspectRatio', 'xMinYMin')

var path = svg.selectAll("path")
  .data(pie(dataset.numbers))
  .enter().append("path")
    .attr("fill", function (d, i) { return color(i); })
    .attr("d", function(d,i){
      return arc(enterClockwise);
    })
    .each(function (d) {
        this._current = {
            data: d.data,
            value: d.value,
            startAngle: enterClockwise.startAngle,
            endAngle: enterClockwise.endAngle
        }
    });

path.transition()
    .duration(750)
    .attrTween("d", arcTween);



var timeout = setTimeout(function () {
    d3.select("input[value=\"oranges\"]").property("checked", true).each(change);
}, 2000);

function createChart() {
    clearTimeout(timeout);
    path = path.data(pie(dataset[this.value]));
    path.enter().append("path")
        .attr("fill", function (d, i) {
            return color(i);
        })
        .attr("d", arc(enterAntiClockwise))
        .each(function (d) {
            this._current = {
                data: d.data,
                value: d.value,
                startAngle: enterAntiClockwise.startAngle,
                endAngle: enterAntiClockwise.endAngle
            };
        }); // store the initial values

    path.exit()
        .transition()
        .duration(750)
        .attrTween('d', arcTweenOut)
        .remove() // now remove the exiting arcs

    path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}

function arcTween(a, j) {
    var i = d3.interpolate(this._current, a);
    this._current = i(0);
    return function (t) {
        return (j === (dataset.numbers.length - 1)) ? arcThin(i(t)) : arc(i(t));
    };
}
function arcTweenOut(a, j) {
    var i = d3.interpolate(this._current, { startAngle: Math.PI * 2, endAngle: Math.PI * 2, value: 0 });
    this._current = i(0);
    return function (t) {
        console.log(j === dataset.length - 1)
        return arc(i(t));
    };
}


function type(d) {
    d.value = +d.value;
    return d;
}

createChart(dataset);
body {
        margin: auto;
        position: relative;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.min.js"></script>
<div id="Donut-chart"></div>


推荐阅读