【问题标题】:Gauge D3.js display value at the top of the needleGauge D3.js 在指针顶部显示值
【发布时间】:2023-03-31 04:09:02
【问题描述】:

我对 D3.js 有点陌生,我设法用我在 Internet 上找到的内容创建了一个仪表。但我找不到任何仪表显示指针顶部的当前值。 像这样的东西:what I want

显然,我希望价值能够随波逐流。我尝试将“文本”属性添加到针本身,但它不起作用。

这是一个 codepen 链接:http://codepen.io/kazu_codepen/pen/wGmGjv?editors=1010

这是我的 js 代码:

// data which need to be fetched

var name = "azerty";

var value = 17;


var gaugeMaxValue = 100; 

// data to calculate 
var percentValue = value / gaugeMaxValue; 

////////////////////////

var needleClient;



(function(){

var barWidth, chart, chartInset, degToRad, repaintGauge,
    height, margin, numSections, padRad, percToDeg, percToRad, 
    percent, radius, sectionIndx, svg, totalPercent, width;



  percent = percentValue;

  numSections = 1;
  sectionPerc = 1 / numSections / 2;
  padRad = 0.025;
  chartInset = 10;

  // Orientation of gauge:
  totalPercent = .75;

  el = d3.select('.chart-gauge');

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

  width = el[0][0].offsetWidth - margin.left - margin.right;
  height = width;
  radius = Math.min(width, height) / 2;
  barWidth = 40 * width / 300;



  //Utility methods 

  percToDeg = function(perc) {
    return perc * 360;
  };

  percToRad = function(perc) {
    return degToRad(percToDeg(perc));
  };

  degToRad = function(deg) {
    return deg * Math.PI / 180;
  };

  // Create SVG element
  svg = el.append('svg').attr('width', width + margin.left + margin.right).attr('height', height + margin.top + margin.bottom);

  // Add layer for the panel
  chart = svg.append('g').attr('transform', "translate(" + ((width + margin.left) / 2) + ", " + ((height + margin.top) / 2) + ")");


  chart.append('path').attr('class', "arc chart-first");
  chart.append('path').attr('class', "arc chart-second");
  chart.append('path').attr('class', "arc chart-third");


  arc3 = d3.svg.arc().outerRadius(radius - chartInset).innerRadius(radius - chartInset - barWidth)
  arc2 = d3.svg.arc().outerRadius(radius - chartInset).innerRadius(radius - chartInset - barWidth)
  arc1 = d3.svg.arc().outerRadius(radius - chartInset).innerRadius(radius - chartInset - barWidth)

  repaintGauge = function () 
  {
    perc = 0.5;
    var next_start = totalPercent;
    arcStartRad = percToRad(next_start);
    arcEndRad = arcStartRad + percToRad(perc / 3);
    next_start += perc / 3;


    arc1.startAngle(arcStartRad).endAngle(arcEndRad);

    arcStartRad = percToRad(next_start);
    arcEndRad = arcStartRad + percToRad(perc / 3);
    next_start += perc / 3;

    arc2.startAngle(arcStartRad + padRad).endAngle(arcEndRad);

    arcStartRad = percToRad(next_start);
    arcEndRad = arcStartRad + percToRad(perc / 3);

    arc3.startAngle(arcStartRad + padRad).endAngle(arcEndRad);

    chart.select(".chart-first").attr('d', arc1);
    chart.select(".chart-second").attr('d', arc2);
    chart.select(".chart-third").attr('d', arc3);


  }
/////////

    var dataset = [{metric:name, value: value}]

    var texts = svg.selectAll("text")
                .data(dataset)
                .enter();

    texts.append("text")
         .text(function(){
              return dataset[0].metric;
         })
         .attr('id', "Name")
         .attr('transform', "translate(" + ((width + margin.left) / 6) + ", " + ((height + margin.top) / 1.5) + ")")
         .attr("font-size",25)
         .style("fill", "#000000");


    texts.append("text")
         .text(function(){
            return dataset[0].value;
         })
         .attr('id', "Value")
         .attr('transform', "translate(" + ((width + margin.left) / 1.4) + ", " + ((height + margin.top) / 1.5) + ")")
         .attr("font-size",25)
         .style("fill", "#000000");




    texts.append("text")
        .text(function(){
            return 0;
        })
        .attr('id', 'scale0')
        .attr('transform', "translate(" + ((width + margin.left) / 100 ) + ", " + ((height + margin.top) / 2) + ")")
        .attr("font-size", 15)
        .style("fill", "#000000");

    texts.append("text")
        .text(function(){
            return gaugeMaxValue/2;
        })
        .attr('id', 'scale10')
        .attr('transform', "translate(" + ((width + margin.left) / 2.15 ) + ", " + ((height + margin.top) / 30) + ")")
        .attr("font-size", 15)
        .style("fill", "#000000");


    texts.append("text")
        .text(function(){
            return gaugeMaxValue;
        })
        .attr('id', 'scale20')
        .attr('transform', "translate(" + ((width + margin.left) / 1.03 ) + ", " + ((height + margin.top) / 2) + ")")
        .attr("font-size", 15)
        .style("fill", "#000000");

  var Needle = (function() {

    //Helper function that returns the `d` value for moving the needle
    var recalcPointerPos = function(perc) {
      var centerX, centerY, leftX, leftY, rightX, rightY, thetaRad, topX, topY;
      thetaRad = percToRad(perc / 2);
      centerX = 0;
      centerY = 0;
      topX = centerX - this.len * Math.cos(thetaRad);
      topY = centerY - this.len * Math.sin(thetaRad);
      leftX = centerX - this.radius * Math.cos(thetaRad - Math.PI / 2);
      leftY = centerY - this.radius * Math.sin(thetaRad - Math.PI / 2);
      rightX = centerX - this.radius * Math.cos(thetaRad + Math.PI / 2);
      rightY = centerY - this.radius * Math.sin(thetaRad + Math.PI / 2);
      return "M " + leftX + " " + leftY + " L " + topX + " " + topY + " L " + rightX + " " + rightY;
    };

    function Needle(el) {
      this.el = el;
      this.len = width / 2.5;
      this.radius = this.len / 8;
    }

    Needle.prototype.render = function() {
      this.el.append('circle').attr('class', 'needle-center').attr('cx', 0).attr('cy', 0).attr('r', this.radius);

        ///////
        /**
        *
        * I tried to add text here
        *
        */
        ///////

      return this.el.append('path').attr('class', 'needle').attr('id', 'client-needle').attr('d', recalcPointerPos.call(this, 0));


    };

    Needle.prototype.moveTo = function(perc) {
      var self,
          oldValue = this.perc || 0;

      this.perc = perc;
      self = this;

      // Reset pointer position
      this.el.transition().delay(100).ease('quad').duration(200).select('.needle').tween('reset-progress', function() {
        return function(percentOfPercent) {
          var progress = (1 - percentOfPercent) * oldValue;

          repaintGauge(progress);
          return d3.select(this).attr('d', recalcPointerPos.call(self, progress));
        };
      });

      this.el.transition().delay(300).ease('bounce').duration(1500).select('.needle').tween('progress', function() {
        return function(percentOfPercent) {
          var progress = percentOfPercent * perc;

          repaintGauge(progress);
          return d3.select(this).attr('d', recalcPointerPos.call(self, progress));
        };
      });

    };


    return Needle;

  })();



  needle = new Needle(chart);
  needle.render();
  needle.moveTo(percent);   

})();

这是我的 html 代码:

<!DOCTYPE html>
<html>
    <head>
        <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
        <style type="text/css" src="gauge.css">
            .chart-gauge
            {
              width: 400px;
              margin: 100px auto  
             } 
            .chart-first
            {
                fill: #9FBD35;
            }
            .chart-second
            {
                fill: #F2BA3A;
            }
            .chart-third
            {
                fill: #FB3033;
            }

            .needle, .needle-center
            {
                fill: #000000;
            }
            .text {
                color: "#112864";
                font-size: 16px;
            }


            svg {
              font: 10px sans-serif;
            }


        </style>

    </head>
    <body>


        <div class="chart-gauge"></div>



        <script type="text/javascript" src="./gaugeClient.js"></script>
        <script type="text/javascript" src="./labels.js"></script>
        <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
    </body>

</html>

如果有人可以提供帮助,我已经花了几天时间尝试没有成功。 谢谢。

【问题讨论】:

    标签: javascript d3.js gauge


    【解决方案1】:

    这是updated codepen

    所做的更改:

    1) 作为初始化的一部分,创建元素valueText

    valueText = chart.append("text")
                    .attr('id', "Value")
                    .attr("font-size",16)
                    .attr("text-anchor","middle")
                    .attr("dy",".5em")
                    .style("fill", '#000000');
    

    2) 同样在初始化时,创建formatValue,以格式化百分比

    formatValue = d3.format('1%');
    

    3) 在过渡的每一帧,计算文本的位置。它将45 单位的偏移量添加到self.len(针的长度)以向外移动文本。

    var thetaRad = percToRad(progress / 2);
    var textX = - (self.len + 45) * Math.cos(thetaRad);
    var textY = - (self.len + 45) * Math.sin(thetaRad);
    

    4) 根据计算出的位置翻译valueText,并从当前progress更新其文本

    valueText.text(formatValue(progress))
      .attr('transform', "translate("+textX+","+textY+")")
    

    【讨论】:

    • 首先感谢您的回答,它工作得很好!如果我想要一个像 1.45 而不是 % 的数字,我应该使用 d3.format(.3) 对吗?
    • 不完全。 d3.format(.2f) 格式化为精度为 2 位小数的浮点数,这将使 1.45。您可能需要将传递给此格式化程序的值乘以 100。
    • @meetamit 没问题,很好的答案!感谢您的贡献!
    • 我刚刚注意到,一旦您开始使用valueText = chart.append("text"),它会生成所有文本,例如低于仪表的名称。知道为什么以及如何显示该名称吗?
    • @kazu,对不起,我不明白这个问题。你能澄清一下你的意思吗?
    【解决方案2】:

    嗯,我发现了一些等待更好的解决方案。

    我正在使用这个:

    var trX = 180 - 210 * Math.cos(percToRad(percent / 2)); 
    var trY = 195 - 210 * Math.sin(percToRad(percent / 2));
    // (180, 195) are the coordinates of the center of the gauge.
    
    displayValue = function() {
                    texts.append("text")
                        .text(function(){
                            return dataset[0].value;
                        })
                        .attr('id', "Value")
                        .attr('transform', "translate(" + trX + ", " + trY+ ")")
                        .attr("font-size",18)
                        .style("fill", '#FB0006');
                }
    

    [...] 然后调用它:

    setTimeout(displayValue, 1350);
    

    这样它不会立即出现,而是在针停止在其位置时。

    它的工作原理是值出现在针的顶部,但我仍然有两个问题。 我真的很希望它在针动画达到其值时一直跟随针,而且我想要比使用通过计算找到的坐标更通用的东西,是否有一个函数可以给出例如,我是仪表的中心?

    我将上面给出的笔编辑为该版本的仪表。 (http://codepen.io/kazu_codepen/pen/wGmGjv?editors=1010)

    无论如何,这个解决方案并不真正符合我的期望,但我会一直使用它,直到找到更好的东西。

    【讨论】:

    • 您好 Kazu,我想知道您是否可以分享您用于查找坐标的计算方法?
    • 好吧,很遗憾我这里没有方法。我首先找到了仪表中心的坐标,然后我尝试了几个值来找到合适的半径,它以某种方式工作。对不起,我帮不上忙。如果有人知道正确计算的方法,我会很感兴趣!
    猜你喜欢
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-08
    • 2015-02-21
    相关资源
    最近更新 更多