D3 线性径向时钟

D3 Linear radial clock

我正在尝试创建一个中间显示小时数的径向折线图。我在渲染文本时遇到问题,如下图(红色突出显示区域)所示。

  var xAxis = svg.selectAll('.radial').append("g");

      var xTick = xAxis
        // .selectAll("g")
        .selectAll(".radial")
        .data(x.ticks(23))
        .enter().append("g")
        .attr("text-anchor", "middle")
        .attr("transform", function(d) {
        return "rotate(" + ((x(d)) * 180 / Math.PI - 90) + ")translate(" + innerRadius + ",0)";
        });

      xTick.append("line")
        .attr("x2", -5)
        .attr("stroke", "#595D5C");

      xTick.append("text")
        .attr("transform", function(d) {
          var angle = x(d.key);
          return ((angle < Math.PI / 2) || (angle > (Math.PI * 3 / 2))) ? "rotate(90)translate(0,22)" : "rotate(-90)translate(0, -15)"; })
            .text(function(d) {
            return d;
      })
      .style("font-size", 10)
      .attr("color", "#595D5C")
      .attr("opacity", 1)

我做错了什么?

const margin = {top: 20, right: 10, bottom: 20, left: 10};

const width = 650 - margin.left - margin.right,
  height = 650 - margin.top - margin.bottom;

const innerRadius = 100,
    outerRadius = Math.min(width, height) / 2 - 6;

const formatHour = d3.timeFormat("%I %p")

const fullCircle = 2 * Math.PI * 23/24;

const y = d3.scaleLinear()
    .range([innerRadius, outerRadius]);
const x = d3.scaleLinear()

    x.range([0, fullCircle]);


    const line = d3.lineRadial()
    .angle(function(d) { return x(d.key); })
    .radius(function(d) { return y(d.value); })
      .curve(d3.curveCardinalClosed)

    data = [];
    for (i=0;i<24;i++){data.push({key:i, value:Math.round(Math.random()*5), class:0})};

    const svg = d3.select("body").append("svg").attr("width", width).attr("height", height);
    const gSelect = svg.selectAll('.radial').data(data);

    gSelect.exit()
    .classed('radial', false)
    .attr('opacity', 0.8)
    .transition()
    .attr('opacity', 0)
    .remove();

    // current.interrupt();

    var gEnter = gSelect.enter().append("g")
    // const g = svg
    // .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
    .classed('radial', true);

    const exit = gSelect.exit().classed('radial', false);
    exit
    .attr('opacity', 0.8)
    .transition()
    .attr('opacity', 0)
    .remove();

    const mean = [];

    x.domain(d3.extent(data, function(d) { return d.key; }));
    y.domain(d3.extent(data, function(d) { return d.value; }));

    // var linePlot = gSelect.append("path")
    var linePlot = d3.selectAll('.radial').append("path")
      .datum(data)
      .attr("fill", "url(#gradientRainbow)")
      .attr("stroke", "#213946")
      .attr("stroke-width", 1)
      .attr('z-index', 200)
      .attr("d", line);

    var numColors = 10;
    var colorScale = d3.scaleLinear()
      .domain([0,(numColors-1)/2,numColors-1])
      .range(["#F5D801", "#74D877", "#2A4858"])
      .interpolate(d3.interpolateHcl);

    var gradient = d3.selectAll('.radial').append("defs").append("radialGradient")
      .attr("id", "gradientRainbow")
      .attr("gradientUnits", "userSpaceOnUse")
      .attr("cx", "0%")
      .attr("cy", "0%")
      .attr("r", "45%")
      .selectAll("stop")
      .data(d3.range(numColors))
      .enter().append("stop")
      .attr("offset", function(d,i) { return (i/(numColors-1)*50 + 40) + "%"; })
      .attr("stop-color", function(d) { return colorScale(d); });

    var yAxis = d3.selectAll('.radial').append("g")
    .attr("text-anchor", "middle");

    var yTick = yAxis
    .selectAll(".radial")
    // .selectAll("g")
    .data(y.ticks(5))
    .enter().append("g");

    yTick.append("circle")
      .attr("fill", "none")
      .attr("stroke", "#D8D8D8")
      .attr("opacity", 0.5)
      .attr("r", function(d) {return y(d)});

    //add avg
    yAxis.append("circle")
      .attr("fill", "none")
      .attr("stroke", "#2A41E5")
      .attr("stroke-width", 3)
      .attr("r", function() { return y(mean) });

    yAxis.append("circle")
      .attr("fill", "white")
      .attr("stroke", "black")
      .attr("opacity", 1)
      .attr("r", function() { return y(y.domain()[0])});

    yTick.append("text")
      .attr("y", function(d) { return -y(d); })
      .attr("dy", "0.35em")
      .text(function(d, i) {
        if (d === 0) {
        return ""
      }
      else {
        return d
      }
    });


      var xAxis = svg.selectAll('.radial').append("g");

      var xTick = xAxis
        // .selectAll("g")
        .selectAll(".radial")
        .data(x.ticks(24))
        .enter().append("g")
        .attr("text-anchor", "middle")
        .attr("transform", function(d) {
        return "rotate(" + ((x(d)) * 180 / Math.PI - 90) + ")translate(" + innerRadius + ",0)";
        });

      xTick.append("line")
        .attr("x2", -5)
        .attr("stroke", "#595D5C");

      xTick.append("text")
        .attr("transform", function(d) {
          var angle = x(d.key);
          return ((angle < Math.PI / 2) || (angle > (Math.PI * 3 / 2))) ? "rotate(90)translate(0,22)" : "rotate(-90)translate(0, -15)"; })
            .text(function(d) {
            return d;
      })
      .style("font-size", 10)
      .attr("color", "#595D5C")
      .attr("opacity", 1)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-scale/1.0.7/d3-scale.min.js"></script>

x 轴的范围定义为 [0, fullCircle],这是一个问题 - 0fullCircle 表示轴上的同一点 (0度数 = 360 度)。

下面是解决该问题的一个非常简单的方法:

const fullCircle = 2 * Math.PI * 23/24;

您可以尝试 运行 代码片段以查看它是否正常工作。