带有正负值的不同环/弧的特殊圆环图

Ris*_*abh 8 html javascript d3.js

我想在D3中创建一种特殊的圆环图,其中包含正负值的不同环.值可以大于100%或小于-100%,因此将有一个代表剩余值的弧.以下是图表的示例图片:在此输入图像描述

第一个正类别(Category_1 - 灰色)值为80,因此80%用灰色填充圆圈,剩下20%用于下一个正类别.下一个正类别值(Category_2 - 橙色)是160.所以它首先使用Category_1剩下的20%(现在剩下140个值).然后将其填充所述下一个圆(向上)100%(现在剩下40值),并且对于剩余的值(40),它被向上创建部分圆.

现在,我们将Category_3(暗红色)视为负(-120%),因此如果创建一个向内的圆并将其填充100%(现在剩下20个值),那么它将为剩余值创建一个向内的弧(20 ).我们有另一个负类别(Category_4 - 红色),所以它会从那里以前的负类别(Category_3)结束开始,并从那里填写20%的面积.

编辑3:我创建了一个非常基本的基于圆弧的圆环图,当总值超过100时,我能够为剩余的值创建外环.下面是JSFiddle链接:

http://jsfiddle.net/rishabh1990/zmuqze80/

data = [20, 240];

var startAngle = 0;
var previousData = 0;
var exceedingData;
var cumulativeData = 0;
var remainder = 100;
var innerRadius = 60;
var outerRadius = 40;
var filledFlag;

var arc = d3.svg.arc()
  .innerRadius(innerRadius)
  .outerRadius(outerRadius)

for (var i = 0; i < data.length; i++) {

  filledFlag = 0;
  exceedingData = 0;

  console.log("---------- Iteration: " + (i + 1) + "---------");

  if (data[i] > remainder) {
    filledFlag = 1;
    exceedingData = data[i] - remainder;
    console.log("Exceeding: " + exceedingData);
    data[i] = data[i] - exceedingData;
    data.splice(i + 1, 0, exceedingData);
  }

  if( filledFlag === 1) {
    cumulativeData = 0;
  } else {
    cumulativeData += data[i];
  }

  console.log("Previous: " + previousData);
  console.log("Data: " + data, "Current Data: " + data[i]);
  var endAngle = (previousData + (data[i] / 50)) * Math.PI;
  console.log("Start " + startAngle, "End " + endAngle);
  previousData = previousData + data[i] / 50;

  //if(i===1) endAngle = 1.4 * Math.PI;
  //if(i===2) endAngle = 2 * Math.PI;

  var vis = d3.select("#svg_donut");

  arc.startAngle(startAngle).endAngle(endAngle);


  vis.append("path")
    .attr("d", arc)
    .attr("transform", "translate(200,200)")
    .style("fill", function(d) {
      if (i === 0) return "red";
      //if (i === 1) return "green";
      //if (i === 2) return "blue"
      //if (i === 3) return "orange"
      //if (i === 4) return "yellow";
    });

  if (exceedingData > 0) {
    console.log("Increasing Radius From " + outerRadius + " To " + (outerRadius + 40));
    outerRadius = outerRadius + 22;
    innerRadius = innerRadius + 22;
    arc.innerRadius(innerRadius).outerRadius(outerRadius);
    console.log("Outer: ", outerRadius);
  }

  if (remainder === 100) {
    remainder = 100 - data[i];
  } else {
    remainder = 100 - cumulativeData;
  };

  if (filledFlag === 1) {
    remainder = 100;
  }

  console.log("Remainder: " + remainder);

  startAngle = endAngle;

}
Run Code Online (Sandbox Code Playgroud)

请分享一些实施的想法.

mee*_*mit 2

好吧,这花了一些时间,但似乎有效。首先,让我们确定您所描述的圆环图也可以使用完全相同的数据呈现为一系列条形图 \xe2\x80\x94。因此,我从那里开始,最终将其制作成圆环图,但也将条形图的实现留在那里。另一件事是,通用解决方案应该能够以任何值包装段,而不仅仅是 100,因此我添加了一个滑块,可让您改变该包装值。最后 \xe2\x80\x94 ,这在条形图而不是甜甜圈实现中更容易解释 \xe2\x80\x94 而不是总是让条形图从左到右换行,就像文本一样,可能需要锯齿形,即交替从左到右然后从右到左缠绕,依此类推。这样做的效果是,当一个金额被分成两条单独的线上的两个部分时,之字形方法将使这两个部分彼此相邻。我添加了一个复选框来打开/关闭这种锯齿形行为。

\n\n

这是一个正在运行的 jsFiddle和它的另一个迭代。

\n\n

以下是重要的部分:

\n\n

有一个函数wrap(data, wrapLength)接受一个data值数组和一个wrapLength包装这些值的函数。该函数计算出哪些数据值必须分成子段,并返回它们的新数组,每个段的对象都有x1,x2y值。x1x2是每个小节的开始和结束,y是小节的行。在圆环图中,这些值相当于起始角度 ( x1)、结束角度 ( x2) 和半径 (y )。

\n\n

该函数wrap()不知道如何考虑负值与正值,因此wrap()必须调用两次 \xe2\x80\x94 ,一次使用所有负值,然后使用所有正值。从那里开始,有选择地仅对底片应用一些处理,然后对两组的组合应用更多处理。最后两段中描述的整个转换集由以下代码片段捕获。我不包括wrap()这里的实现,只是调用它的代码;也不包括渲染代码,一旦segments生成就非常简单。

\n\n
// Turn N data points into N + x segments, as dictated by wrapLength. Do this separately\n// for positive and negative values. They\'ll be merged further down, after we apply\n// a specific transformation to just the negatives\nvar positiveSegments = wrap(data.filter(function(d) { return d.value > 0; }), wrapLength);\nvar negativeSegments = wrap(data.filter(function(d) { return d.value < 0; }), wrapLength);\n\n// Flip and offset-by-one the y-value of every negative segment. I.e. 0 becomes -1, 1 becomes -2\nnegativeSegments.forEach(function(segment) { segment.y = -(segment.y + 1); });\n\n// Flip the order of the negative segments, so that their sorted from negative-most y-value and up\nnegativeSegments.reverse()\n\n// Combine negative and positive segments\nsegments = negativeSegments.concat(positiveSegments);\n\nif(zigzag) {\n  segments.forEach(function(segment) {\n    if(Math.abs(segment.y) % 2 == (segment.y < 0 ? 0 : 1)) { flipSegment(segment, wrapLength); }\n  });\n}\n\n// Offset the y of every segment (negative or positive) so that the minimum y is 0\n// and goes up from there\nvar maxNegativeY = negativeSegments[0].y * -1;\nsegments.forEach(function(segment) { segment.y += maxNegativeY; });\n
Run Code Online (Sandbox Code Playgroud)\n