score:1

Accepted answer

I would use a text path for the percent text and use an arc as the template so that you don't have to worry about manually transforming the text and calculating the angle. This means reorganising your elements slightly and using arc3 (currently unused) as the path for the text.

The general format for text on a path is:

<path id="path_for_text" d="M-150,1.8369701987210297e-14A150,150 0 0,1 18.799985034645633,-148.8172051971717L13.45243373590199,-106.4869779410873A107.33333333333334,107.33333333333334 0 0,0 -107.33333333333334,1.3144542310848258e-14Z"></path>
<text>
    <textPath xlink:href="#path_for_text">my text here</textPath>
</text>

so the basic alterations that we'll need to do on your code are adding the new arc for the text to go along, and adding in the text path element. So, let's create an appropriate arc generator:

 // we want the text to be offset slightly from the outer edge of the arc, and the arc
 // itself can have identical inner and outer radius measurements
 var arc3 = d3.svg.arc()
   .outerRadius(radius - chartInset + 10)
   .innerRadius(radius - chartInset + 10)

 // add the text element and give it a `textPath` element as a child
 var arc_text = chart.append('text')
    .attr('id', 'scale10')
    .attr("font-size", 15)
    .style("fill", "#000000")

 // the textPath element will use an element with ID `text_arc` to provide its shape
 arc_text.append('textPath')
    .attr('startOffset','0%')
    .attr('xlink:href', '#text_arc' )

 // add the path with the ID `text_arc`
 chart.append('path').attr('class', "arc chart-third")
    .attr('id', 'text_arc')

In repaintGauge, calculate the appropriate arc:

 // append the path to the chart, using the arc3 constructor to generate the arc
 // these numbers will be the same as those for arc2, although I would add a little
 // padding to both start and end angles to ensure that the text doesn't wrap if it
 // is at 0% or 100%
 arc3.startAngle(arcStartRad - 0.15).endAngle(arcEndRad + 0.15);

 chart.select('id', 'text_arc')
  .attr('d', arc3)

and update the text:

arc_text.select('textPath')
   .text( percent + '%')

You can refactor your repaintGauge function to make it significantly simpler as some of the arc figures don't change; arc1's startAngle will always be at 1.5 Pi radians, and arc2's endAngle will always be 2.5 Pi radians. That means you only need to work out what your percent is in terms of radians, which is pretty simple: if 0% is 1.5 Pi and 100% is 2.5 Pi, and you want to represent perc percent, it will be p / 100 * Math.PI + 1.5 * Math.PI.

repaintGauge = function(perc) {
  var arcOffset = Math.PI * 1.5
  var current = Math.PI * perc / 100 + arcOffset

  // arc1's endAngle and arc2, arc3's endAngle can be set to `current`
  arc1.startAngle(arcOffset).endAngle(current)

  arc2.startAngle(current + padRad).endAngle(arcOffset + Math.PI)
  arc3.startAngle(current - 0.15).endAngle(arcOffset + Math.PI + 0.15)

  chart.select(".chart-first").attr('d', arc1);
  chart.select(".chart-second").attr('d', arc2);
  chart.select(".chart-third").attr('d', arc3);
  arc_text.select('textPath').text(perc + '%');
};

Here's a demo showing the text at different positions and with different values:

var name = "Value";

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;
  
  var percStart = 0;
  var arcOffset = Math.PI * 1.5

  // Orientation of gauge:
  totalPercent = .75;

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

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

  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) + ")");
  
  formatValue = d3.format('1%');

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

  // bind angle data directly to the chart elements
  chart.append('path').attr('class', "arc chart-first")
    .datum({ startAngle: arcOffset, endAngle: arcOffset })
    .attr('d', arc1)
  chart.append('path').attr('class', "arc chart-second")
    .datum({ startAngle: arcOffset, endAngle: arcOffset + padRad + Math.PI })
    .attr('d', arc2)
  chart.append('path').attr('class', "arc chart-third")
    .attr('id', 'text_arc')
    .datum({ startAngle: arcOffset - 0.15, endAngle: arcOffset + Math.PI + 0.15 })
    .attr('d', arc3)

  var arc_text = chart.append('text')
    .attr('id', 'scale10')
    .attr("font-size", 15)
    .style("fill", "#000000")
    .attr('text-anchor', 'start')
    
  arc_text.append('textPath')
    .attr('startOffset','0%')
    .attr('xlink:href', '#text_arc' )

  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) / 2) + ", " + ((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 + "%";
    })
    .attr('id', 'scale20')
    .attr('transform', "translate(" + ((width + margin.left) / 1.08) + ", " + ((height + margin.top) / 2) + ")")
    .attr("font-size", 15)
    .style("fill", "#000000");

  repaintGauge = function(perc) {
      var current = Math.PI * perc / 100 + arcOffset
      var t = d3.transition().duration(500)
      
      chart.select(".chart-first")
        .transition(t)
        .attrTween('d', arcEndTween(current, arc1));

      chart.select(".chart-second")
        .transition(t)
        .attrTween('d', arcStartTween(current, arc2));

      chart.select(".chart-third")
        .transition(t)
        .attrTween('d', arcStartTween(current, arc3) );

      arc_text.select('textPath')
        .text( perc.toFixed(1) + '%')


  }

  function arcStartTween(newAngle, arc) {
    return function(d) {
      var interpolate = d3.interpolate(d.startAngle, newAngle);
      return function(t) {
        d.startAngle = interpolate(t);
        return arc(d);
      };
    };
  }
  function arcEndTween(newAngle, arc) {
    return function(d) {
      var interpolate = d3.interpolate(d.endAngle, newAngle);
      return function(t) {
        d.endAngle = interpolate(t);
        return arc(d);
      };
    };
  }

    /////////


  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);

      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;
          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;
          return d3.select(this).attr('d', recalcPointerPos.call(self, progress));
        };
      });

    };


    return Needle;

  })();

  setInterval(function() {
    repaintGauge( Math.floor(Math.random() * 100) )
  }, 1500);

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


})();
  .chart-gauge
            {
              width: 400px;
              margin: 100px auto  
             } 
            .chart-first
            {
                fill: #66AB8C;
            }
            .chart-second
            {
                fill: #ff533d;
            }
  

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


            svg {
              font: 10px sans-serif;
            }
<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="style.css">
    
  </head>

  <body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.min.js"></script>
<div class="chart-gauge"></div>
  </body>

</html>


Related Query

More Query from same tag