d3v4 Saw Chart -- 半饼图示例和基于圆弧的示例

d3v4 Saw Chart -- half pie chart example and arc based example

我对创建以下模型很感兴趣——其中只呈现了一半的饼图——并且这些段在此曲线中聚成一团。

我已经调整了半饼图来调用类似的方法——但我不确定如何修改圆弧示例以产生相同且更理想的效果——因为没有使用 d3.pie设置 start/end 角度。我正在操纵弧的 start/end 角度,但不确定如何稍微调整它 - 而不会破坏它。 你能做点什么吗

                var arc = d3.arc()
                        .startAngle(-90 * (Math.PI / 180))
                .endAngle(120 * (Math.PI / 180))

//只有普通饼图功能的版本 https://jsfiddle.net/wqLzbhud/

//当前版本的弧长相同但高度不同。 https://jsfiddle.net/wqLzbhud/1/

            function getArc(){
                var arc = d3.arc()
                    .innerRadius(function(d, i){
                        return radius-innerradius;
                    })
                    .outerRadius(function(d){
                        var maxHeight = height/2;
                        var ratio = (d.value/maxHeight * 100)+ radius;
                        return ratio;
                    })
                    .startAngle(function(d, i){
                        return d.startAngle;
                    })
                    .endAngle(function(d, i){
                        return d.endAngle;
                    });

                    return arc;
            }


            function setData(data, isSorted){                   
                var displacement = 0;
                var arcPartition = 2*Math.PI/data.length;



                $.each(data, function(ri, value) {
                    var startAngle = (ri*arcPartition);
                    var endAngle = ((ri+1)*arcPartition);

                    if(ri!=0){
                        startAngle+=displacement;
                        endAngle+=displacement;
                    }

                    data[ri]["startAngle"] = startAngle;
                    data[ri]["endAngle"] = endAngle;       
                });

                return data;
            }

最新代码库 2020 年 6 月 17 日

https://jsfiddle.net/gv368fmk/3/

如本文档中所述,我们可以使用弧度来指定角度。

http://using-d3js.com/05_07_arcs_pie_charts.html

From Documentation: Angles are specified in radians where 0 radians is at 12 o’clock and positive radians trace a path clockwise
0 - 12 O'clock
Math.PI/4 - Half-past one
Math.PI/2 - 3 O'clock
3 * Math.PI/4 - Half-past four
Math.PI - 6 O'clock
5 * Math.PI/4 - Half-past seven
3 * Math.PI/2 - 9 O'clock
7 * Math.PI/4 - Half-past ten
2 * Math.PI - It's again at 12 O'clock

Same works for negative radians, just it will be anti-clockwise

所以在你的情况下,它将是:

.startAngle(-Math.PI / 2)
.endAngle((3 * Math.PI) / 4)

负值将从逆时针方向开始角度。

为了生成不同高度的段,我们可以根据需要以任何方式为每个段修改它,而不是仅仅提供上面创建的弧对象。

// .attr("d", arc)
.attr("d", function (d, i) {
  return arc.outerRadius(data[i].outerRadius)(d, i);
})

看看下面的例子,我刚刚修改了你上面分享的jsfiddle例子。如果您有任何问题,请告诉我。

$(document).ready(function() {
        var $this = $(".sawchart");

        var data = [{
            label: "Jam",
            value: 5
          },
          {
            label: "Coconut",
            value: 15
          },
          {
            label: "Nutmeg",
            value: 30
          },
          {
            label: "Tumeric",
            value: 50
          },
        ];

        var width = $this.data("width"),
          height = $this.data("height"),
          radius = $this.data("r"),
          innerradius = $this.data("ir");

        var color = d3
          .scaleOrdinal()
          .range(["#f0cf85", "#e7f0c3", "#a4d4ae", "#32afa9"]);

        var arc = d3
          .arc()
          .outerRadius(radius)
          .innerRadius(innerradius);

        var labelArc = d3
          .arc()
          .outerRadius(radius - 40)
          .innerRadius(radius - 40);

        var total = 0
        data.forEach(function(d) {
          total += d.value;
        });


    //add outer Radius to d object
        data.forEach(function(d) {
          d.outerRadius = ((d.value / total) * 100) + radius;
        });

        /**
         * Notice I have used Math.PI to generate angle
         * From Documentation: Angles are specified in radians where 0 radians is at 12 o’clock and positive radians trace a path clockwise
         * 0 - 12 O'clock
         * Math.PI/4 - Half past one
         * Math.PI/2 - 3 O'clock
         * 3 * Math.PI/4 - Half past four
         * Math.PI - 6 O'clock
         * 5 * Math.PI/4 - Half past seven
         * 3 * Math.PI/2 - 9 O'clock
         * 7 * Math.PI/4 - Half past ten
         * 2 * Math.PI - It's again 12 O'clock
         * 
         * Same works for negative radians, just it will be anti-clockwise
         */
        var pie = d3
          .pie()
          .startAngle(-Math.PI / 2)
          .endAngle((3 * Math.PI) / 4)
          .sort(null)
          .value(function(d) {
            return total;
          });

        var pieData = pie(data);

        var svg = d3
          .select($this[0])
          .append("svg")
          .attr("width", width)
          .attr("height", height)
          .append("g")
          .attr("class", "piechart")
          .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

        var segments = svg.append("g").attr("class", "segments");

        var slices = segments
          .selectAll(".arc")
          .data(pieData)
          .enter()
          .append("g")
          .attr("class", "arc");

        /**
         * Here instead of just providing arc created above, we can modify it for each segment in whatever manner we want to.
         * I have changed OuterRadius for each from Data
         */
        slices
          .append("path")
          // .attr("d", arc)
          .attr("d", function(d, i) {
            return arc.outerRadius(d.data.outerRadius)(d, i);
          })
          .attr("fill", function(d, i) {
            return color(i);
          });

        //__labels
        var labels = svg.append("g").attr("class", "labels");

        var label = labels
          .selectAll("text")
          .data(pieData)
          .enter()
          .append("text")
          .attr("text-anchor", "middle");

        label
          .attr("x", function(d) {
            var a = d.startAngle + (d.endAngle - d.startAngle) / 2 - Math.PI / 2;
            d.cx = Math.cos(a) * (innerradius + (d.data.outerRadius - innerradius) / 2);
            return (d.x = Math.cos(a) * (d.data.outerRadius +  20));
          })
          .attr("y", function(d) {
            var a = d.startAngle + (d.endAngle - d.startAngle) / 2 - Math.PI / 2;
            d.cy = Math.sin(a) * (innerradius + (d.data.outerRadius - innerradius) / 2);
            return (d.y = Math.sin(a) * (d.data.outerRadius +  20));
          })
          .text(function(d) {
            return d.data.label;
          })
          .each(function(d) {
            var bbox = this.getBBox();
            d.sx = d.x - bbox.width / 2 - 2;
            d.ox = d.x + bbox.width / 2 + 2;
            d.sy = d.oy = d.y + 5;
          })
          .transition()
          .duration(300);

        labels.transition().duration(300);

        labels.exit().remove();
        //__labels

        //__pointers
        var pointers = svg.append("g").attr("class", "pointers");

        pointers
          .append("defs")
          .append("marker")
          .attr("id", "circ")
          .attr("markerWidth", 6)
          .attr("markerHeight", 6)
          .attr("refX", 3)
          .attr("refY", 3)
          .append("circle")
          .attr("cx", 3)
          .attr("cy", 3)
          .attr("r", 3);

        var pointer = pointers
          .selectAll("path.pointer")
          .data(pieData)
          .enter()
          .append("path")
          .attr("class", "pointer")
          .style("fill", "none")
          .style("stroke", "black")
          .attr("marker-end", "url(#circ)");

        pointer
          .attr("d", function(d) {
            if (d.cx > d.ox) {
              return (
                "M" +
                d.sx +
                "," +
                d.sy +
                "L" +
                d.ox +
                "," +
                d.oy +
                " " +
                d.cx +
                "," +
                d.cy
              );
            } else {
              return (
                "M" +
                d.ox +
                "," +
                d.oy +
                "L" +
                d.sx +
                "," +
                d.sy +
                " " +
                d.cx +
                "," +
                d.cy
              );
            }
          })
          .transition()
          .duration(300);

        pointers.transition().duration(300);

        pointers.exit().remove();
        //__pointers
      });
body {
    background: #ffd;
   }

   .arc text {
    font: 10px sans-serif;
    text-anchor: middle;
   }

   .arc path {
    stroke: #fff;
   }
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

<h1>SawChart I</h1>

<!--html-->
<div
  class="sawchart"
  data-width="500"
  data-height="500"
  data-r="110"
  data-ir="60"
/>

---编辑--- 我根据输入值创建了一个动态的外半径,而不是从输入中指定它。

//add outer Radius to d object
data.forEach(function(d) {
  d.outerRadius = ((d.value / total) * 100) + radius;
});
.
.
.
.attr("d", function(d, i) {
  return arc.outerRadius(d.data.outerRadius)(d, i);
})

另外,以同样的方式更新标签的位置:

label
.attr("x", function(d) {
  var a = d.startAngle + (d.endAngle - d.startAngle) / 2 - Math.PI / 2;
  d.cx = Math.cos(a) * (innerradius + (d.data.outerRadius - innerradius) / 2);
  return (d.x = Math.cos(a) * (d.data.outerRadius +  20));
})
.attr("y", function(d) {
  var a = d.startAngle + (d.endAngle - d.startAngle) / 2 - Math.PI / 2;
  d.cy = Math.sin(a) * (innerradius + (d.data.outerRadius - innerradius) / 2);
  return (d.y = Math.sin(a) * (d.data.outerRadius +  20));
})