|
function Gauge(placeholderName, configuration) |
|
{ |
|
this.placeholderName = placeholderName; |
|
|
|
var self = this; // for internal d3 functions |
|
|
|
this.configure = function(configuration) |
|
{ |
|
this.config = configuration; |
|
|
|
this.config.size = this.config.size * 0.9; |
|
|
|
this.config.raduis = this.config.size * 0.97 / 2; |
|
this.config.cx = this.config.size / 2; |
|
this.config.cy = this.config.size / 2; |
|
|
|
this.config.min = undefined != configuration.min ? configuration.min : 0; |
|
this.config.max = undefined != configuration.max ? configuration.max : 100; |
|
this.config.range = this.config.max - this.config.min; |
|
|
|
this.config.majorTicks = configuration.majorTicks || 5; |
|
this.config.minorTicks = configuration.minorTicks || 2; |
|
|
|
this.config.greenColor = configuration.greenColor || "#109618"; |
|
this.config.yellowColor = configuration.yellowColor || "#FF9900"; |
|
this.config.redColor = configuration.redColor || "#DC3912"; |
|
|
|
this.config.transitionDuration = configuration.transitionDuration || 500; |
|
} |
|
|
|
this.render = function() |
|
{ |
|
this.body = d3.select("#" + this.placeholderName) |
|
.append("svg:svg") |
|
.attr("class", "gauge") |
|
.attr("width", this.config.size) |
|
.attr("height", this.config.size); |
|
|
|
this.body.append("svg:circle") |
|
.attr("cx", this.config.cx) |
|
.attr("cy", this.config.cy) |
|
.attr("r", this.config.raduis) |
|
.style("fill", "#ccc") |
|
.style("stroke", "#000") |
|
.style("stroke-width", "0.5px"); |
|
|
|
this.body.append("svg:circle") |
|
.attr("cx", this.config.cx) |
|
.attr("cy", this.config.cy) |
|
.attr("r", 0.9 * this.config.raduis) |
|
.style("fill", "#fff") |
|
.style("stroke", "#e0e0e0") |
|
.style("stroke-width", "2px"); |
|
|
|
for (var index in this.config.greenZones) |
|
{ |
|
this.drawBand(this.config.greenZones[index].from, this.config.greenZones[index].to, self.config.greenColor); |
|
} |
|
|
|
for (var index in this.config.yellowZones) |
|
{ |
|
this.drawBand(this.config.yellowZones[index].from, this.config.yellowZones[index].to, self.config.yellowColor); |
|
} |
|
|
|
for (var index in this.config.redZones) |
|
{ |
|
this.drawBand(this.config.redZones[index].from, this.config.redZones[index].to, self.config.redColor); |
|
} |
|
|
|
if (undefined != this.config.label) |
|
{ |
|
var fontSize = Math.round(this.config.size / 9); |
|
this.body.append("svg:text") |
|
.attr("x", this.config.cx) |
|
.attr("y", this.config.cy / 2 + fontSize / 2) |
|
.attr("dy", fontSize / 2) |
|
.attr("text-anchor", "middle") |
|
.text(this.config.label) |
|
.style("font-size", fontSize + "px") |
|
.style("fill", "#333") |
|
.style("stroke-width", "0px"); |
|
} |
|
|
|
var fontSize = Math.round(this.config.size / 16); |
|
var majorDelta = this.config.range / (this.config.majorTicks - 1); |
|
for (var major = this.config.min; major <= this.config.max; major += majorDelta) |
|
{ |
|
var minorDelta = majorDelta / this.config.minorTicks; |
|
for (var minor = major + minorDelta; minor < Math.min(major + majorDelta, this.config.max); minor += minorDelta) |
|
{ |
|
var point1 = this.valueToPoint(minor, 0.75); |
|
var point2 = this.valueToPoint(minor, 0.85); |
|
|
|
this.body.append("svg:line") |
|
.attr("x1", point1.x) |
|
.attr("y1", point1.y) |
|
.attr("x2", point2.x) |
|
.attr("y2", point2.y) |
|
.style("stroke", "#666") |
|
.style("stroke-width", "1px"); |
|
} |
|
|
|
var point1 = this.valueToPoint(major, 0.7); |
|
var point2 = this.valueToPoint(major, 0.85); |
|
|
|
this.body.append("svg:line") |
|
.attr("x1", point1.x) |
|
.attr("y1", point1.y) |
|
.attr("x2", point2.x) |
|
.attr("y2", point2.y) |
|
.style("stroke", "#333") |
|
.style("stroke-width", "2px"); |
|
|
|
if (major == this.config.min || major == this.config.max) |
|
{ |
|
var point = this.valueToPoint(major, 0.63); |
|
|
|
this.body.append("svg:text") |
|
.attr("x", point.x) |
|
.attr("y", point.y) |
|
.attr("dy", fontSize / 3) |
|
.attr("text-anchor", major == this.config.min ? "start" : "end") |
|
.text(major) |
|
.style("font-size", fontSize + "px") |
|
.style("fill", "#333") |
|
.style("stroke-width", "0px"); |
|
} |
|
} |
|
|
|
var pointerContainer = this.body.append("svg:g").attr("class", "pointerContainer"); |
|
|
|
var midValue = (this.config.min + this.config.max) / 2; |
|
|
|
var pointerPath = this.buildPointerPath(midValue); |
|
|
|
var pointerLine = d3.line() |
|
.x(function(d) { return d.x }) |
|
.y(function(d) { return d.y }) |
|
.curve(d3.curveBasis); |
|
|
|
pointerContainer.selectAll("path") |
|
.data([pointerPath]) |
|
.enter() |
|
.append("svg:path") |
|
.attr("d", pointerLine) |
|
.style("fill", "#dc3912") |
|
.style("stroke", "#c63310") |
|
.style("fill-opacity", 0.7) |
|
|
|
pointerContainer.append("svg:circle") |
|
.attr("cx", this.config.cx) |
|
.attr("cy", this.config.cy) |
|
.attr("r", 0.12 * this.config.raduis) |
|
.style("fill", "#4684EE") |
|
.style("stroke", "#666") |
|
.style("opacity", 1); |
|
|
|
var fontSize = Math.round(this.config.size / 10); |
|
pointerContainer.selectAll("text") |
|
.data([midValue]) |
|
.enter() |
|
.append("svg:text") |
|
.attr("x", this.config.cx) |
|
.attr("y", this.config.size - this.config.cy / 4 - fontSize) |
|
.attr("dy", fontSize / 2) |
|
.attr("text-anchor", "middle") |
|
.style("font-size", fontSize + "px") |
|
.style("fill", "#000") |
|
.style("stroke-width", "0px"); |
|
|
|
this.redraw(this.config.min, 0); |
|
} |
|
|
|
this.buildPointerPath = function(value) |
|
{ |
|
var delta = this.config.range / 13; |
|
|
|
var head = valueToPoint(value, 0.85); |
|
var head1 = valueToPoint(value - delta, 0.12); |
|
var head2 = valueToPoint(value + delta, 0.12); |
|
|
|
var tailValue = value - (this.config.range * (1/(270/360)) / 2); |
|
var tail = valueToPoint(tailValue, 0.28); |
|
var tail1 = valueToPoint(tailValue - delta, 0.12); |
|
var tail2 = valueToPoint(tailValue + delta, 0.12); |
|
|
|
return [head, head1, tail2, tail, tail1, head2, head]; |
|
|
|
function valueToPoint(value, factor) |
|
{ |
|
var point = self.valueToPoint(value, factor); |
|
point.x -= self.config.cx; |
|
point.y -= self.config.cy; |
|
return point; |
|
} |
|
} |
|
|
|
this.drawBand = function(start, end, color) |
|
{ |
|
if (0 >= end - start) return; |
|
|
|
this.body.append("svg:path") |
|
.style("fill", color) |
|
.attr("d", d3.arc() |
|
.startAngle(this.valueToRadians(start)) |
|
.endAngle(this.valueToRadians(end)) |
|
.innerRadius(0.65 * this.config.raduis) |
|
.outerRadius(0.85 * this.config.raduis)) |
|
.attr("transform", function() { return "translate(" + self.config.cx + ", " + self.config.cy + ") rotate(270)" }); |
|
} |
|
|
|
this.redraw = function(value, transitionDuration) |
|
{ |
|
var pointerContainer = this.body.select(".pointerContainer"); |
|
|
|
pointerContainer.selectAll("text").text(Math.round(value)); |
|
|
|
var pointer = pointerContainer.selectAll("path"); |
|
pointer.transition() |
|
.duration(undefined != transitionDuration ? transitionDuration : this.config.transitionDuration) |
|
//.delay(0) |
|
//.ease("linear") |
|
//.attr("transform", function(d) |
|
.attrTween("transform", function() |
|
{ |
|
var pointerValue = value; |
|
if (value > self.config.max) pointerValue = self.config.max + 0.02*self.config.range; |
|
else if (value < self.config.min) pointerValue = self.config.min - 0.02*self.config.range; |
|
var targetRotation = (self.valueToDegrees(pointerValue) - 90); |
|
var currentRotation = self._currentRotation || targetRotation; |
|
self._currentRotation = targetRotation; |
|
|
|
return function(step) |
|
{ |
|
var rotation = currentRotation + (targetRotation-currentRotation)*step; |
|
return "translate(" + self.config.cx + ", " + self.config.cy + ") rotate(" + rotation + ")"; |
|
} |
|
}); |
|
} |
|
|
|
this.valueToDegrees = function(value) |
|
{ |
|
// thanks @closealert |
|
//return value / this.config.range * 270 - 45; |
|
return value / this.config.range * 270 - (this.config.min / this.config.range * 270 + 45); |
|
} |
|
|
|
this.valueToRadians = function(value) |
|
{ |
|
return this.valueToDegrees(value) * Math.PI / 180; |
|
} |
|
|
|
this.valueToPoint = function(value, factor) |
|
{ |
|
return { x: this.config.cx - this.config.raduis * factor * Math.cos(this.valueToRadians(value)), |
|
y: this.config.cy - this.config.raduis * factor * Math.sin(this.valueToRadians(value)) }; |
|
} |
|
|
|
// initialization |
|
this.configure(configuration); |
|
} |