Search code examples
javascriptd3.jsc3.jsgauge

How to customise Gauge needle pointer using D3.js or C3.js?


I need to plot a Gauge in my project. I am using d3.js to plot a gauge. I Followed this link

It works good but i need to customise the needle point. I searched quite a lot but i could not fond any solution to customise the needle pointer.

This is my result:

Result

Expected Result:

Expected

How to achieve it using d3.js or c3.js.If anyone has idea share with me. Thanks in advance.


Solution

  • Based on the link you have provided you need to edit :

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

    In this code the append circle is the center of the needle and the path is the needle tip. The d positions are calculated using the recalcPointerPos function

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

    As you can see the return part of this function sends the coordinates for the path for the needle tip. Editing this return values will let you customize it.

    Update: more details of the mathematics involved in this can be found on this page by Jake Trent.

    I hope this helps. For more detailed explanation, please share your own code so that we can review it specifically.

    Update: Alternatively, here is a simpler solution based on the comments by the question creator:

    Just append a white circle on top of the guage with an appropriate radius. Put this after the needle and append it to the chart:

    chart.append("circle")
        .attr("r", 70)
        .style("fill", "white")
        ;
    

    Check out this plunker: https://plnkr.co/edit/25KSME35LewdkQaybBc5?p=preview

    Update 2 : Thought up a better but slightly more complicated way of doing the same as above but using SVG Masks. In this method, you avoid creating a white circle, thereby if you place the gauge on a different background, it will not show the white circle. This method utilizes masks. We have to define a mask with two circles:

    1. A circle with fill of white - This is the area which will be shown i.e. the radius will be that of the entire gauge.
    2. A circle with a fill of black - This is the area which will be hidden/masked.

    This mask should be defined after the arc paths are appended as:

    chart.append("defs")
        .append("mask")
        .attr ("id", "mask-1")
        .append("circle")
        .attr("r", 180)
        .style("fill", "#FFF")
        ;
    d3.select("#mask-1")
          .append("circle")
        .attr("r", 70)
        .style("fill", "#000");
    

    Next we need to add this mask to the needle and the needle center as:

    Needle.prototype.render = function() {
     this.el.append('circle').attr('class', 'needle-center').attr('cx', 0).attr('cy', 0).attr('r', this.radius)
    .attr("mask", "url(#mask-1)");
    
      return this.el.append('path').attr('class', 'needle').attr('id', 'client-needle').attr('d', recalcPointerPos.call(this, 0))
    .attr("mask", "url(#mask-1)");
    };
    

    Check this plunker out for a demo of this method. https://plnkr.co/edit/4SQSNO?p=preview