如何创建仅在每个段的一端具有圆角边缘的圆环图?

How can I create a doughnut chart with rounded edges only on one end of each segment?

我正在尝试构建一个仅在一侧具有圆角边缘的圆环图。我的问题是我双面都圆了,而不仅仅是一侧。也不知道如何做更多的前景弧,而不仅仅是一个。

    const tau = 2 * Math.PI; // http://tauday.com/tau-manifesto
    const arc = d3.arc()
        .innerRadius(80)
        .outerRadius(100)
        .startAngle(0)
        .cornerRadius(15);
    const svg = d3.select("svg"),
        width = +svg.attr("width"),
        height = +svg.attr("height"),
        g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

背景弧,但我不确定是否需要这个?

const background = g.append("path")
        .datum({endAngle: tau})
        .style("fill", "#ddd")
        .attr("d", arc);

    const data = [ .51];
    const c = d3.scaleThreshold()
          .domain([.200,.205,.300,.310, .501, 1])
          .range(["green","#ddd", "orange","#ddd", "red"]);
    Const pie = d3.pie()
          .sort(null)
          .value(function(d) {
            return d;
          });

只有一个前景,但需要能够有多个:

 const  foreground = g.selectAll('.arc')
        .data(pie(data))
        .enter()
        .append("path")
        .attr("class", "arc")
        .datum({endAngle: 3.8})
        .style("fill", function(d) {
            return c(d.value);
          })
        .attr("d", arc)

我做错了什么?

var tau = 2 * Math.PI; // http://tauday.com/tau-manifesto

// An arc function with all values bound except the endAngle. So, to compute an
// SVG path string for a given angle, we pass an object with an endAngle
// property to the `arc` function, and it will return the corresponding string.
var arc = d3.arc()
    .innerRadius(80)
    .outerRadius(100)
    .startAngle(0)
    .cornerRadius(15);

// Get the SVG container, and apply a transform such that the origin is the
// center of the canvas. This way, we don’t need to position arcs individually.
var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

// Add the background arc, from 0 to 100% (tau).
var background = g.append("path")
    .datum({endAngle: tau})
    .style("fill", "#ddd")
    .attr("d", arc);

var data = [ .51];
var c = d3.scaleThreshold()
      .domain([.200,.205,.300,.310, .501, 1])
      .range(["green","#ddd", "orange","#ddd", "red"]);
var pie = d3.pie()
      .sort(null)
      .value(function(d) {
        return d;
      });
// Add the foreground arc in orange, currently showing 12.7%.
var foreground = g.selectAll('.arc')
    .data(pie(data))
    .enter()
    .append("path")
    .attr("class", "arc")
    .datum({endAngle: 3.8})
    .style("fill", function(d) {
        return c(d.value);
      })
    .attr("d", arc)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="960" height="500"></svg>

documentation 指出,圆角半径应用于圆弧的两端。此外,您希望圆弧重叠,但事实并非如此。

您可以通过以下方式添加 one-sided 圆角:

  1. 使用没有角半径的圆弧 arc 作为数据。
  2. 为圆角添加额外的 path 个对象 corner。这些需要转移到每个 arc.
  3. 的末尾
  4. 由于corner两边都有圆角,所以添加一个clipPath来剪掉这个弧的一半。 clipPath 包含每个 cornerpath。这对于小于圆角长度两倍的弧是必不可少的。
  5. raise corner 的所有元素到前面,然后 sort 它们按索引降序排列,以便它们以正确的方式重叠。

const arc = d3.arc()
    .innerRadius(50)
    .outerRadius(70);
const arc_corner = d3.arc()
    .innerRadius(50)
    .outerRadius(70)
    .cornerRadius(10);

const svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

const clipPath = g.append("clipPath")
  .attr("id", "clip_corners");

const c = d3.scaleQuantile()
  .range(["#f7fcf0","#e0f3db","#ccebc5","#a8ddb5","#7bccc4","#4eb3d3","#2b8cbe","#08589e"]);

const pie = d3.pie().value(d => d);

function render(values) {
  c.domain(values);
  const arcs = pie(values);
  const corners = pie(values).map(d => {
    d.startAngle = d.endAngle - 0.2;
    d.endAngle = d.endAngle + 0.2;
    return d;
  });
  const clip = pie(values).map(d => {
    d.startAngle = d.endAngle - 0.01;
    d.endAngle = d.endAngle + 0.2;
    return d;
  });

  g.selectAll(".arc")
      .data(arcs)
      .join("path")
        .attr("class", "arc")
        .style("fill", d => c(d.value))
        .attr("d", arc);

  clipPath.selectAll("path")
    .data(clip)
    .join("path")
      .attr("d", arc);

  g.selectAll(".corner")
    .data(corners)
    .join("path")
      .raise()
      .attr("class", "corner")
      .attr("clip-path", "url(#clip_corner)")
      .style("fill", d => c(d.value))
      .attr("d", arc_corner)
      .sort((a, b) => b.index - a.index);
}

function randomData() {
  const num = Math.ceil(8 * Math.random()) + 2;
  const values = Array(num).fill(0).map(d => Math.random());
  render(values);
}

d3.select("#random_data")
  .on("click", randomData);

 randomData();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
<button id="random_data">
Random data
</button>
<svg width="150" height="150"></svg>

我将依赖更改为当前版本的 d3。