class FluidGauge {
constructor(elementId, value, config) {
this.text1, this.text2;
this.elementId = elementId;
this.value = value;
if (config == null) { this.config = FluidGauge.liquidFillGaugeDefaultSettings(); } else { this.config = config; }
config = this.config;
var gauge = d3.select('#' + this.elementId);
gauge.node().gauge = this;
var radius = Math.min(parseInt(gauge.style("width")), parseInt(gauge.style("height")))/2;
var locationX = parseInt(gauge.style("width"))/2 - radius;
var locationY = parseInt(gauge.style("height"))/2 - radius;
var fillPercent = Math.max(config.minValue, Math.min(config.maxValue, value))/config.maxValue;
var waveHeightScale;
if(config.waveHeightScaling) {
waveHeightScale = d3.scaleLinear()
.range([0,config.waveHeight,0])
.domain([0,50,100]);
} else {
waveHeightScale = d3.scaleLinear()
.range([config.waveHeight, config.waveHeight])
.domain([0,100]);
}
this.waveHeightScale = waveHeightScale;
var textPixels = (config.textSize*radius/2);
var textFinalValue = parseFloat(value).toFixed(2);
var textStartValue = config.valueCountUp?config.minValue:textFinalValue;
this.percentText = config.displayPercent?"%":"";
var circleThickness = config.circleThickness * radius;
var circleFillGap = config.circleFillGap * radius;
var fillCircleMargin = circleThickness + circleFillGap;
var fillCircleRadius = radius - fillCircleMargin;
var waveHeight = fillCircleRadius * waveHeightScale(fillPercent*100);
this.fillCircleRadius = fillCircleRadius;
this.fillCircleMargin = fillCircleMargin;
var waveLength = fillCircleRadius*2 / config.waveCount;
var waveClipCount = 1+config.waveCount;
var waveClipWidth = waveLength * waveClipCount;
var textRounder = function(value) { return Math.round(value); };
this.waveClipWidth = waveClipWidth;
if(parseFloat(textFinalValue) != parseFloat(textRounder(textFinalValue))) {
textRounder = function(value) { return parseFloat(value).toFixed(1); };
}
var data = [];
for(var i=0; i<= 40 * waveClipCount; i++) {
data.push({x:i/(40*waveClipCount), y: (i/40)});
}
var gaugeCircleX = d3.scaleLinear().range([0,2*Math.PI]).domain([0,1]);
var gaugeCircleY = d3.scaleLinear().range([0,radius]).domain([0,radius]);
var waveScaleX = d3.scaleLinear().range([0,waveClipWidth]).domain([0,1]);
var waveScaleY = d3.scaleLinear().range([0,waveHeight]).domain([0,1]);
var waveRiseScale = d3.scaleLinear()
.range([(fillCircleMargin+fillCircleRadius*2+waveHeight),(fillCircleMargin-waveHeight)])
.domain([0,1]);
var waveAnimateScale = d3.scaleLinear()
.range([0, waveClipWidth-fillCircleRadius*2])
.domain([0,1]);
this.waveAnimateScale = waveAnimateScale;
var textRiseScaleY = d3.scaleLinear()
.range([fillCircleMargin+fillCircleRadius*2, (fillCircleMargin+textPixels*0.7)])
.domain([0,1]);
var gaugeGroup = gauge.append("g")
.attr("transform","translate("+locationX+","+locationY+")");
var gaugeCircleArc = d3.arc()
.startAngle(gaugeCircleX(0))
.endAngle(gaugeCircleX(1))
.outerRadius(gaugeCircleY(radius))
.innerRadius(gaugeCircleY(radius-circleThickness));
gaugeGroup.append("path")
.attr("d",gaugeCircleArc)
.style("fill",config.circleColor)
.attr("transform","translate("+radius+","+radius+")");
this.text1 = gaugeGroup.append("text")
.text(textRounder(textStartValue)+this.percentText)
.attr("class","liquidFillGaugeText")
.attr("text-anchor","middle")
.attr("font-size",textPixels + "px")
.style("fill",config.textColor)
.attr("transform","translate("+radius+","+textRiseScaleY(config.textVertPosition)+")");
var clipArea = d3.area()
.x(function(d){ return waveScaleX(d.x); })
.y0(function(d) { return waveScaleY(Math.sin(Math.PI*2*config.waveOffset*-1 + Math.PI*2*(1-config.waveCount) + d.y*2*Math.PI));} )
.y1(function(d) { return (fillCircleRadius*2 + waveHeight)});
this.clipArea = clipArea;
var waveGroup = gaugeGroup.append("defs")
.append("clipPath")
.attr("id","clipWave"+elementId);
this.waveGroup = waveGroup;
var wave = waveGroup.append("path")
.datum(data)
.attr("d",clipArea)
.attr("T",0);
this.wave = wave;
var fillCircleGroup = gaugeGroup.append("g")
.attr("clip-path","url(#clipWave"+elementId+")");
fillCircleGroup.append("circle")
.attr("cx",radius)
.attr("cy",radius)
.attr("r",fillCircleRadius)
.style("fill",config.waveColor);
this.text2 = fillCircleGroup.append("text")
.text(textRounder(textStartValue)+this.percentText)
.attr("class","liquidFillGaugeText")
.attr("text-anchor","middle")
.attr("font-size",textPixels+"px")
.style("fill",config.waveTextColor)
.attr("transform","translate("+radius+","+textRiseScaleY(config.textVertPosition)+")");
if(config.valueCountUp) {
var pt = this.percentText;
var textTween = function(){
var that = this;
var i = d3.interpolate(this.textContent, textFinalValue);
return function(t) {
that.textContent = textRounder(i(t)) + pt;
}
}
this.text1.transition()
.duration(config.waveRiseTime)
.tween("text",textTween);
this.text2.transition()
.duration(config.waveRiseTime)
.tween("text",textTween);
}
var waveGroupXPosition = fillCircleMargin+fillCircleRadius*2-waveClipWidth;
this.waveGroupXPosition = waveGroupXPosition;
if(config.waveRise) {
waveGroup.attr("transform","translate("+waveGroupXPosition+","+waveRiseScale(0)+")")
.transition()
.duration(config.waveRiseTime)
.attr("transform","translate("+waveGroupXPosition+","+waveRiseScale(fillPercent)+")")
.on("start",function(){
wave.attr("transform","translate(1,0)");
});
} else {
waveGroup.attr("transform","translate("+waveGroupXPosition+","+waveRiseScale(fillPercent)+")");
}
this.animateWave = function() {
var that = this;
wave.attr("transform","translate("+waveAnimateScale(wave.attr("T"))+",0)");
wave.transition()
.duration(config.waveAnimateTime*(1-wave.attr('T')))
.ease(d3.easeLinear)
.attr("transform","translate("+waveAnimateScale(1)+",0)")
.on("end",function(){
wave.attr("T",0);
that.animateWave();
});
}
if(config.waveAnimate) { this.animateWave(); }
gauge.on('click',function(){
var g = this.gauge;
g.update(newValue());
});
}
static liquidFillGaugeDefaultSettings(){
return {
minValue: 0,
maxValue: 100,
circleThickness: 0.05,
circleFillGap: 0.05,
circleColor: "#178BCA",
waveHeight: 0.2,
waveCount: 5,
waveRiseTime: 1000,
waveAnimateTime: 1000,
waveRise: true,
waveHeightScaling: true,
waveAnimate: true,
waveColor: "#178BCA",
waveOffset: 0,
textVertPosition: .5,
textSize: 1.5,
valueCountUp: true,
displayPercent: true,
textColor: "#045681",
waveTextColor: "#A4DBf8"
};
}
}