Skip to content

Instantly share code, notes, and snippets.

@ameyms
Last active September 15, 2021 11:28
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ameyms/9184728 to your computer and use it in GitHub Desktop.
Save ameyms/9184728 to your computer and use it in GitHub Desktop.
D3 Gauge - Part Deux

Yet another simple gauge chart using d3 Inspired by Jake Trent's Codepen snippet

To move the pointer needle, type in following code in you javascript console

needle.moveTo(.25)
var needle;
(function(){
var barWidth, chart, chartInset, degToRad, repaintGauge,
height, margin, numSections, padRad, percToDeg, percToRad,
percent, radius, sectionIndx, svg, totalPercent, width;
percent = .65;
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-filled");
chart.append('path').attr('class', "arc chart-empty");
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)
{
var next_start = totalPercent;
arcStartRad = percToRad(next_start);
arcEndRad = arcStartRad + percToRad(perc / 2);
next_start += perc / 2;
arc1.startAngle(arcStartRad).endAngle(arcEndRad);
arcStartRad = percToRad(next_start);
arcEndRad = arcStartRad + percToRad((1 - perc) / 2);
arc2.startAngle(arcStartRad + padRad).endAngle(arcEndRad);
chart.select(".chart-filled").attr('d', arc1);
chart.select(".chart-empty").attr('d', arc2);
}
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 / 3;
this.radius = this.len / 6;
}
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('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);
})();
<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<style type="text/css">
.chart-gauge
{
width: 400px;
margin: 100px auto
}
.chart-filled
{
fill: steelblue;
}
.chart-empty
{
fill: #dedede;
}
.needle, .needle-center
{
fill: #464A4F;
}
svg {
font: 10px sans-serif;
}
</style>
</head>
<body>
<div class="chart-gauge"></div>
<script type="text/javascript" src="d3_gauge.js"></script>
<script type="text/javascript">
setInterval(function(){
needle.moveTo(Math.random());
}, 5000);
</script>
</body>
</html>
@s2t2
Copy link

s2t2 commented Sep 13, 2020

Hey @ameyms, very cool. Quick question - to start the dial at 50% instead of 0%, what variables / values would need to be changed? I changed recalcPointerPos.call(this, 0) on line 109 from 0 to 0.5 and that is where it starts briefly, but then it seems it keeps starting back at 0.
Thanks!

@nilesh1762
Copy link

Plz can Anyone help me How can I do this same in reactHook.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment