How to customise Gauge needle pointer using D3.js

2019-07-19 20:04发布

问题:

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:

Expected Result:

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

回答1:

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