Skip to content

Instantly share code, notes, and snippets.

@detain
Created May 18, 2014 08:42
Show Gist options
  • Save detain/bb323c6188213cb645b2 to your computer and use it in GitHub Desktop.
Save detain/bb323c6188213cb645b2 to your computer and use it in GitHub Desktop.
A Pen by Joe Huss.
<!--
12 Dec 2013:
Plunker went down, got unstable, and the embedded preview failed.
So I'm putting up the same demo here temporarily.
Unfortunate that CodePen cannot have multiple files, so code here looks a bit messy,
not that I intend it to be, but putting this up is priority.
I'll clean this up when I have time...
-->
<script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script data-require="jqueryui@*" data-semver="1.10.0" src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.0/jquery-ui.js"></script>
<script data-require="raphael@*" data-semver="2.1.0" src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<link data-require="jqueryui@*" data-semver="1.10.0" rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.0/css/smoothness/jquery-ui-1.10.0.custom.min.css" />
<div class="demo">
<!--
<p>Note: There seems to be a transformation rotation precision issue in IE8. Applying this <a href="https://groups.google.com/forum/?fromgroups=#!msg/raphaeljs/sYY-CTMw6DM/USHIDoKSc0cJ" target="_blank">patch to Raphael</a> seems to address the issue. While there are no CDN</p>
-->
<h1>jQuery SVG Gauge Plugin</h1>
<p>You don't need to learn Raphael.js to use this.</p>
<p>This jQuery plugin uses Raphael.js internally to draw customizable SVG Gauges, and allows you to set/get values by simply using .val()</p>
<p>View README for details</p>
<p>Blogged at: <a href="http://terryyoung.blogspot.hk/2013/12/jquery-svg-gauge-plugin-10.html" target="_blank">http://terryyoung.blogspot.hk/2013/12/jquery-svg-gauge-plugin-10.html</a></p>
<p>Click on each gauge to view sample code.</p>
<div class="metal-border speedometer-half" id="example1A">
<div class="example"></div>
</div>
<div class="code" id="code_example1A"></div>
<script type="text/javascript" class="example" id="script_example1A">
// default functionality
$(document).ready(function() {
$('#example1A div.example').svgGauge({
'value': 75
});
});
</script>
<div class="metal-border speedometer-half" id="example1B">
<div class="example"></div>
<div class="gauge-value"></div>
<div class="gauge-percentage"></div>
</div>
<div class="code" id="code_example1B"></div>
<script type="text/javascript" class="example" id="script_example1B">
// positive startingIncline, symmetrical speedometer
$(document).ready(function() {
$('#example1B div.example').svgGauge({
'value': 75,
'minValue': 0,
'maxValue': 500,
'startingIncline': 30,
'minorTicks': {
'count': 1
},
'valuePrecision': 0,
'percentagePrecision': 0,
'hand': {
'centerPointRadius': 2
},
'onAnimate': function(data, gauge) {
var $this = $(this),
$parent = $this.parent(),
$value = $parent.find('div.gauge-value'),
$percentage = $parent.find('div.gauge-percentage');
$value.html(data.value + '/' + gauge.maxValue);
$percentage.html((data.percentage * 100).toFixed(0) + '%');
}
});
});
</script>
<div class="metal-border speedometer-half" id="example1C">
<div class="example"></div>
</div>
<div class="code" id="code_example1C"></div>
<script type="text/javascript" class="example" id="script_example1C">
// symmetrical speedometer, mimic full zone area fill
$(document).ready(function() {
$('#example1C div.example').svgGauge({
'value': 75,
'startingIncline': 0,
'minorTicks': {
'count': 1
},
'zones': {
'normal': {
'attr': {
'stroke-width': 70,
'opacity': 0.3
}
},
'warning': {
'attr': {
'stroke-width': 70,
'opacity': 0.3
}
},
'critical': {
'attr': {
'stroke-width': 70,
'opacity': 0.3
}
}
}
});
});
</script>
<div class="metal-border speedometer-half" id="example1D">
<div class="example"></div>
<div class="gauge-value"></div>
</div>
<div class="code" id="code_example1D"></div>
<script type="text/javascript" class="example" id="script_example1D">
// two color zones example, asymmetrical speedometer
$(document).ready(function() {
$('#example1D div.example').svgGauge({
'value': 75,
'startingIncline': 0,
'endingIncline': 150,
'margin': 10,
'majorTicks': {
'count': 10,
'length': 10,
'attr': {
'stroke-width': 2
}
},
'minorTicks': {
'count': 1,
'length': 4,
'attr': {
'stroke-width': 2
}
},
'zones': {
'normal': {
'show': false,
'fromPercentage': 0,
'toPercentage': 0.3
},
'warning': {
'fromPercentage': 0.3,
'toPercentage': 0.7,
'attr': {
'stroke': '#FFA100',
'stroke-width': 5
}
},
'critical': {
'fromPercentage': 0.7,
'toPercentage': 1,
'attr': {
'stroke-width': 10
}
}
},
'hand': {
'centerPointRadius': 6,
'length': 45
},
'onAnimate': function(data, gauge) {
var $this = $(this),
$parent = $this.parent(),
$value = $parent.find('div.gauge-value');
$value.html(data.value.toFixed(0));
}
});
});
</script>
<br class="clear" />
<br class="clear" />
<div class="metal-border speedometer-3quarters" id="example1E">
<div class="example"></div>
<div class="gauge-percentage"></div>
<div class="gauge-overlay normal"></div>
<div class="gauge-status normal"></div>
</div>
<div class="code" id="code_example1E"></div>
<script type="text/javascript" class="example" id="script_example1E">
// negative startingIncline symmetrical speedometer
$(document).ready(function() {
$('#example1E div.example').svgGauge({
'value': 75,
'startingIncline': -30,
'percentagePrecision': 0,
'onZoneChange': function(zone, data, gauge) {
var $this = $(this),
$parent = $this.parent(),
$status = $parent.find('div.gauge-status'),
$overlay = $parent.find('div.gauge-overlay');
$status.add($overlay).removeClass('normal warning critical')
.addClass(zone);
$status.html(zone);
},
'onAnimate': function(data, gauge) {
var $this = $(this),
$parent = $this.parent(),
$percentage = $parent.find('div.gauge-percentage');
$percentage.html('CPU: ' + data.percentageString);
}
});
});
</script>
<div class="metal-border speedometer-3quarters" id="example1F">
<div class="example"></div>
</div>
<div class="code" id="code_example1F"></div>
<script type="text/javascript" class="example" id="script_example1F">
// asymmetrical speedometer example
$(document).ready(function() {
$('#example1F div.example').svgGauge({
'value': 100,
'minValue': 0,
'maxValue': 270,
'startingIncline': -90,
'endingIncline': 180,
'margin': 15,
'majorTicks': {
'count': 6,
'length': 10,
'attr': {
'stroke': '#023',
'stroke-width': 3
}
},
'minorTicks': {
'count': 1,
'attr': {
'stroke': '#003'
}
},
'hand': {
'centerPointRadius': 2,
'length': 50,
'attr': {
'stroke': '#003'
}
},
'zones': {
'normal': {
'fromPercentage': 0,
'toPercentage': 3 / 6,
'attr': {
'stroke': '#018905',
'stroke-width': 4
}
},
'warning': {
'show': false,
'fromPercentage': 3 / 6,
'toPercentage': 5 / 6
},
'critical': {
'fromPercentage': 5 / 6,
'toPercentage': 1,
'attr': {
'stroke-width': 10
}
}
}
});
});
</script>
<div class="metal-border speedometer-3quarters" id="example1G">
<div class="example"></div>
<div class="gauge-value"></div>
</div>
<div class="code" id="code_example1G"></div>
<script type="text/javascript" class="example" id="script_example1G">
// thermometer example:
// blue zone represents negative values
// red zone represents values over 40
$(document).ready(function() {
$('#example1G div.example').svgGauge({
'value': 25.5,
'minValue': -20,
'maxValue': 60,
'startingIncline': -60,
'endingIncline': 180,
'margin': 8,
'majorTicks': {
'count': 8,
'length': 20,
'attr': {
'stroke': '#023',
'stroke-width': 3
}
},
'minorTicks': {
'count': 4,
'attr': {
'stroke': '#023'
}
},
'hand': {
'centerPointRadius': 2,
'length': 50,
'attr': {
'stroke': '#003'
}
},
'zones': {
'normal': {
'fromPercentage': 0,
'toPercentage': 2 / 8,
'attr': {
'stroke': '#10E5ED',
'stroke-width': 20
}
},
'warning': {
'fromPercentage': 2 / 8,
'toPercentage': 60 / 80,
'attr': {
'stroke': '#cdcdcd',
'stroke-width': 20
}
},
'critical': {
'fromPercentage': 60 / 80,
'toPercentage': 1,
'attr': {
'stroke-width': 20
}
}
},
'onAnimate': function(data, gauge) {
var $this = $(this),
$parent = $this.parent(),
$value = $parent.find('div.gauge-value');
$value.html(data.value.toFixed(1) + '&deg;C');
}
});
});
</script>
<div class="metal-border speedometer-3quarters" id="example1H">
<div class="example"></div>
</div>
<div class="code" id="code_example1H"></div>
<script type="text/javascript" class="example" id="script_example1H">
// thermometer example:
// blue zone represents negative values
// red zone represents values over 40
$(document).ready(function() {
$('#example1H div.example').svgGauge({
'value': 25.5,
'minValue': -30,
'maxValue': 60,
'startingIncline': 0,
'endingIncline': 270,
'margin': 15,
'majorTicks': {
'count': 9,
'length': 20,
'attr': {
'stroke': '#023',
'stroke-width': 3
}
},
'minorTicks': {
'count': 1,
'attr': {
'stroke': '#023'
}
},
'hand': {
'centerPointRadius': 2,
'length': 50,
'attr': {
'stroke': '#003'
}
},
'zones': {
'normal': {
'fromPercentage': 0,
'toPercentage': 1 / 3,
'attr': {
'stroke-width': 6
}
},
'warning': {
'fromPercentage': 1 / 3,
'toPercentage': 2 / 3,
'attr': {
'stroke-width': 6
}
},
'critical': {
'fromPercentage': 2 / 3,
'toPercentage': 1,
'attr': {
'stroke-width': 20
}
}
}
});
});
</script>
<br class="clear" />
<div class="demo">
<p>Value setters and getters</p>
<div class="default-gauge" id="example2">
<div class="example"></div>
</div>
Type any value from 0 to 100
<br>
<input type="text" maxlength="3" size="3" id="example2_val" autocomplete="off" value="100">
<button id="example2_set">Set</button>
<button id="example2_get">Get</button>
<div class="code" id="code_example2"></div>
<script type="text/javascript" class="example" id="script_example2">
$(document).ready(function() {
$('#example2').svgGauge({
'value': 100,
'minValue': 0,
'maxValue': 100,
'startingIncline': -90,
'endingIncline': 180,
'arc': {
'attr': {
'stroke': '#005d01'
}
},
'minorTicks': {
'attr': {
'stroke': '#005d01'
}
},
'majorTicks': {
'attr': {
'stroke': '#005d01'
}
},
'hand': {
'attr': {
'fill': '#00CA02'
}
}
});
$('#example2_set').on('click', function() {
var newVal = $('#example2_val').val();
$('#example2').val(newVal);
});
$('#example2_get').on('click', function() {
alert($('#example2').val());
});
});
</script>
</div>
</div>
<br class="clear" />
<script type="text/javascript">
/**
* The following is *NOT* usage code of jQuery SVG Gauge,
* it converts actual sample scripts into tooltips.
*/
$(document).ready(function() {
var $demos = $('div.demo');
$('div.example').each(function(i, el) {
var $example = $(el),
id = $example.parent().attr('id'),
scriptID = 'script_' + id,
codeID = 'code_' + id,
$script = $('#' + scriptID),
$code = $('#' + codeID);
$code
.html('<pre>' + $script.text() + '</pre>')
.hide()
.on('click', 'div.close', function(event) {
$(this).closest('div.code').hide();
});
$example.parent().on('click', function(event) {
var $this = $(this),
$code = $('#code_' + $this.attr('id'));
$('div.code').hide();
$('div.demo-close').remove();
$code.show().position({
my: 'left top',
at: 'center center',
of: $this
});
$('<div class="demo-close"></div>').prependTo('body').position({
my: 'right top',
at: 'right+10 top-10',
of: $code
});
});
});
$('body').on('click', 'div.demo-close', function(event) {
$('div.demo-close').remove();
$('div.code').hide();
});
function demo() {
$demos.first().find('div.example').svgGauge('randomize');
}
window.demoTimer = setInterval(demo, 2000);
});
</script>

jQuery SVG Gauge Plugin

jQuery SVG Gauge Plugin

You don't need to learn Raphael.js to use this.

This jQuery plugin uses Raphael.js internally to draw customizable SVG Gauges, and allows you to set/get values by simply using .val()

License

WTFPL

Dependencies

  • jQuery 1.9 or above
  • Raphael.js 2.1.1 or above - as the 'Transformation Rotation Precision issue was resolved', see [DmitryBaranovskiy/raphael#653]
  • You will also need geometry.js and raphael-extensions.js I included in this Plunker

Some notable features

  • Let's you create an SVG Gauge in jQuery fashion, and does the RaphaelJS work for you
  • Get and Set gauge values using .val()
  • onAnimate and onZoneChange event handlers
  • Configurable start and end angles of the gauge
  • Configurable zones of the gauge
  • Cosmetic aspects of the gauge are exposed as options, as much as possible. The thickness, length, color of major and minor tick marks, gauge hand, gauge arc, zones, etc

Forked from Terry Young's Pen jQuery SVG Gauge Plugin.

A Pen by Joe Huss on CodePen.

License.

/**
*
* These are a collection of Geometry functions under the namespace "geom"
*
* Well, it is good enough for what I currently need, I'm not aiming to build an exhaustive library.
*
* @author Terry Young <terryyounghk [at] gmail.com>
* @access public
*
*/
var geom = {
/**
* Distance Formula
*/
getDistance: function(a, b) {
return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
},
/**
* Mid-Point Formula
*/
getMidPoint: function(a, b) {
return {
x: (a.x + b.x) / 2,
y: (a.y + b.y) / 2
};
},
/**
* Returns an object containing all bounding-box related information
* relative to an SVG coordinate system, i.e. top-left is 0,0
* @param w
* @param h
* @param cx (Optional)
* @param cy (Optional)
* @param undefined (Optional) (Do not pass in this parameter)
* @return {Object}
*/
getBoundingBox: function(w, h, cx, cy, undefined) {
if (cx === undefined && cy === undefined) {
cx = w / 2;
cy = h / 2;
}
return {
width: w,
height: h,
center: {
x: cx,
y: cy
},
top: {
x: cx,
y: cy - h / 2
},
bottom: {
x: cx,
y: cy + h / 2
},
left: {
x: cx - w / 2,
y: cy
},
right: {
x: cx + w / 2,
y: cy
},
topLeft: {
x: cx - w / 2,
y: cy - h / 2
},
topRight: {
x: cx + w / 2,
y: cy - h / 2
},
bottomLeft: {
x: cx - w / 2,
y: cy + h / 2
},
bottomRight: {
x: cx + w / 2,
y: cy + h / 2
}
};
},
/**
* Check if point C is the mid-point of A and B
*/
isMidPoint: function(a, b, c) {
return geom.coordinatesAreEqual(c, geom.getMidPoint(a, b));
},
/**
* Gradient Formula (i.e. Slope)
*/
getSlope: function(a, b) {
return (b.y - a.y) / (b.x - a.x);
},
/**
* Get the angle of the slope produced by coordinates A and B.
* It convert the Inverse-Tangent of the slope to Degrees and returns it.
*/
getAngle: function(a, b) {
return geom.toDegrees(Math.atan(geom.getSlope(a, b)));
},
/**
* Accepts any number of coordinate objects as arguments and return true they are all collinear.
* i.e. all points lie on the same straight line.
*/
isCollinear: function() {
var a = arguments;
if (a.length <= 1) {
// hmm... what to do here, should we throw an error?
} else {
var s1 = geom.getSlope(a[0], a[1]); // get the first slope
for (var i = 1, j = a.length - 1; i < j; i++) {
var s2 = geom.getSlope(a[i], a[i + 1]);
if (s1 != s2) {
return false;
}
return true;
}
}
},
/**
*
* Gets the number of distinct diagonals of a polygon
*
* @author Terry Young <terryyounghk [at] gmail.com>
* @access public
*
* @param v
* number of vertices of the polygon
*
*/
getDiagonals: function(v) {
return (v * (v - 3)) / 2;
},
/**
* This function returns the x,y coordinates where two lines L1 and L2 intersect, given that
* L1 is defined by two points A(x1,y1) and B(x2,y2), and L2 is defined by two points C(x3,y3) and D(x4,y4).
*
* "g" is an optional parameter.
*
* When g is false, then our aim is to return a line-segment intersection,
* (i.e. L1 and L2 are line segments, and may or may not intersect even when they are parallel)
* where as when true, we find the line-line intersection.
* (i.e. L1 and L2 are infinitely long lines, that the only case they do not collide is when they are parellel,
* and that if there's an intersection, it does not necessarily situate at the point within any of the two line segments)
*
* @credits This is a modified version inspired by
* http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/1968345#1968345
*/
getIntersection: function(a, b, c, d, g) {
var s1_x = b.x - a.x,
s1_y = b.y - a.y,
s2_x = d.x - c.x,
s2_y = d.y - c.y;
var s = (-s1_y * (a.x - c.x) + s1_x * (a.y - c.y)) / (-s2_x * s1_y + s1_x * s2_y),
t = (s2_x * (a.y - c.y) - s2_y * (a.x - c.x)) / (-s2_x * s1_y + s1_x * s2_y);
g = !! g; // convert to boolean
// if these are NaN, we've found a collinear case
// if any of these are +Inifinity or -Infinity,
var x = a.x + (t * s1_x),
y = a.y + (t * s1_y);
if ((g || (s >= 0 && s <= 1 && t >= 0 && t <= 1))) { // && isFinite(x) && isFinite(y)) {
// Collision detected
return {
x: x,
y: y
};
}
return false; // No collision
},
/**
* Helper function: returns an {x: ..., y: ...} object by accepting two values
*/
point: function(x, y) {
return {
'x': x,
'y': y
};
},
/**
* Helper function: takes in a coordinate and returns a formatted string. e.g. 5,-11
* "cts" means Coordinates To String
*/
cts: function(c) {
return [c.x, c.y].join(',');
},
/**
* Helper function: takes in two {x: ... y: ...} objects and see if they are equal
*/
coordinatesAreEqual: function(a, b) {
return (a.x === b.x && a.y === b.y);
},
/**
* Helper function: expects a Radian and converts to Degress
*/
toDegrees: function(rad) {
return rad * 180 / Math.PI;
},
/**
* Helper function: expects a Radian and converts to Degress
*/
toRadians: function(deg) {
return deg * Math.PI / 180;
}
};
/**
*
* This is an extension to RaphaelJS for drawing Arcs
*
* @author Terry Young <terryyounghk [at] gmail.com>
* @access public
*
*/
window.RaphaelExtensions = {
ca: {
/**
* @usage make an arc at 50,50 with a radius of 30 that grows from 0 to 40 of 100 with a bounce
* var my_arc = archtype.path().attr({
* "stroke": "#f00",
* "stroke-width": 14,
* arc: [50, 50, 0, 100, 50]
* });
*
* my_arc.animate({
* arc: [50, 50, amount, 100, 50]
* }, 1500, "bounce");
*
* @param cx
* @param cy
* @param value
* @param total
* @param radius
* @param startingIncline
* @param endingIncline
* @return {Object}
*/
gaugeArc: function (cx, cy, value, total, radius, startingIncline, endingIncline) {
startingIncline = (startingIncline === undefined)
? 0
: Math.max(-89, Math.min(89, startingIncline));
endingIncline = (startingIncline === undefined)
? 180 - startingIncline
: Math.max(91, Math.min(269, endingIncline));
var alpha = Math.abs(endingIncline - startingIncline) / total * value,
a = (180 - alpha) * Math.PI / 180,
x = cx + radius * Math.cos(a),
y = cy - radius * Math.sin(a),
path;
path = [
["M", cx - radius, cy],
["A", radius, radius,
0,
+(alpha > 180),
1,
x,
y]
];
return {
path: path
};
},
gaugeArcZone: function (cx, cy, fromPercentage, toPercentage, radius, startingIncline, endingIncline) {
var arc = this.paper.path().attr({
gaugeArc: [cx, cy, 100, 100, radius, startingIncline, endingIncline]
}),
length = arc.getTotalLength(),
path = arc.getSubpath(length * fromPercentage, length * toPercentage);
arc.remove();
return {
path: path
}
}
}
};
/**
* jQuery SVG Gauge Plugin 1.0.0
*
* This jQuery plugin requires Raphael 2.x
*
* License: WTFPL
* Copyright (C) 2013 Terry Young <terryyounghk at gmail dot com>
*/
(function($, Raphael, RaphaelExtensions, undefined) {
/**
* The SVGGauge instance will be stored in this.data(dataKey) for internal reference
* @type {String}
*/
var dataKey = 'jquery-svg-gauge';
/**
* Available methods of .svgGauge()
* @type {Object}
*/
var methods = {
/**
* Initializes the svgGauge
* @param options
* @return {*} chaining
*/
init: function(options) {
return this.each(function(i, el) {
var $el = $(el);
$el.data(dataKey, new SVGGauge($el, options));
});
},
/**
* Returns the svgGauge instance
* @return {*}
*/
widget: function() {
return this.data(dataKey);
},
/**
* Returns the Raphael paper object, in case you ever need to access it directly
*/
paper: function() {
return (this.data(dataKey)) ? this.data(dataKey).paper : undefined;
},
/**
* Sets/Gets the value of the svgGauge
*
* Obsolete. As we are going to duck-punch $.fn.val below, you can then just use $('#gauge').val() to set/get the value.
*
* @param value
* @return {*}
*/
value: function(value) {
var instance = this.data(dataKey);
return (value === undefined) ? instance.val() : instance.val(value);
},
/**
* Returns the minValue of the svgGauge
* @return {int}
*/
minValue: function() {
return (this.data(dataKey)) ? this.data(dataKey).minValue : undefined;
},
/**
* Returns the maxValue of the svgGauge
* @return {int}
*/
maxValue: function() {
return (this.data(dataKey)) ? this.data(dataKey).maxValue : undefined;
},
/**
*
* @return {*}
*/
shutdown: function() {
return this.each(function(i, el) {
$(el).data(dataKey).shutdown();
});
},
/**
* For demo. Sets a random value to the svgGauge
* @return {*}
*/
randomize: function() {
return this.each(function(i, el) {
$(el).data(dataKey).randomize();
});
}
};
$.fn.svgGauge = function(method) {
// Method calling logic
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.svgGauge');
return undefined;
}
};
// -----------------------------------------------------------------------------------------------------------------
// DUCK PUNCH !!!!!!!!!!!!!!!!!!!!
//
// Usages:
//
// $('#gauge').val(100); // Sets the value of the svgGauge to 100
// alert($('#gauge').val()); // alerts '100'
//
var _oldval = $.fn.val;
$.fn.val = function(value) {
if (value === undefined) {
// return the first svgGauge's value
var instance = this.first().data(dataKey);
if (instance instanceof SVGGauge) {
return instance.val();
} else {
// else, let the original .val() kick in
return _oldval.apply(this, arguments);
}
} else {
// apply the value to all elements in the set
return this.each(function(i, el) {
var $el = $(el),
instance = $el.data(dataKey);
if (instance instanceof SVGGauge) {
instance.val(value);
} else {
_oldval.apply($el, [value]);
}
});
}
};
//
// QUACK !!!!!!!!!!!!!!!!!!!!
// -----------------------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------
// Beyond this line is the internal SVGGauge constructor and prototype
// The default properties are within the SVGGauge's init() method
//
var NORMAL = 1,
WARNING = 2,
CRITICAL = 3,
ZONES = [];
// map integer values to text strings
ZONES[NORMAL] = 'normal';
ZONES[WARNING] = 'warning';
ZONES[CRITICAL] = 'critical';
function SVGGauge(jqObject, options) {
this.init();
$.extend(true, this, this.defaults); // First extend the new instance with the defaults
$.extend(true, this, options || {}); // Then extend the new instance with options, if specified, which overrides the defaults
this.originalOptions = options; // reserved
this.container = jqObject.get(0); // required by Raphael: http://raphaeljs.com/reference.html#Raphael
this._validateOptions(); // some values need to be ensured they are within acceptable ranges
this.create();
this.draw();
// prevent from overriding
if (options && options['value'] !== undefined) {
this.value = this.minValue;
this.val(options.value);
}
this.testing = function() {
console.warn(this.value);
}
}
SVGGauge.prototype = {
init: function() {
var msg;
if (!geom) {
msg = 'Geom JavaScript Library is required for the jQuery svgGauge plugin.';
} else if (!Raphael) {
msg = 'Raphael JavaScript Library is required for the jQuery svgGauge plugin.';
} else if (!Raphael.type) {
msg = 'Your browser does not support vector graphics.';
} else {
this.defaults = {
'value': 0,
'minValue': 0,
'maxValue': 100,
'valuePrecision': 2 // the maximum number of decimals for returned values
,
'percentagePrecision': 0 // the maximum number of decimals for returned percentages
,
'width': 150 // The width of the Raphael paper
,
'height': 150 // The height of the Raphael paper
,
'margin': 7
// 90
// |
// 0 _____|_____
// |
// -90
,
'startingIncline': 0,
'endingIncline': undefined,
'majorTicks': {
'show': true,
'count': 5,
'length': 15,
'attr': {
'stroke': '#000000', // translates to SVG stroke
'opacity': 1,
'stroke-width': 2 // translates to SVG stroke-width
}
},
'minorTicks': {
'show': true,
'count': 5,
'length': 7,
'attr': {
'stroke': '#000000', // translates to SVG stroke
'opacity': 1,
'stroke-width': 1 // translates to SVG stroke-width
}
},
'arc': {
'show': true,
'attr': {
'stroke': '#000000', // translates to SVG stroke
'opacity': 1,
'stroke-width': 2 // translates to SVG stroke-width
}
},
'zones': {
'normal': {
'show': true,
'fromPercentage': 0,
'toPercentage': 0.6,
'attr': {
'stroke': '#27ff1d', // translates to SVG stroke
'opacity': 1,
'stroke-width': 7 // translates to SVG stroke-width
}
},
'warning': {
'show': true,
'fromPercentage': 0.6,
'toPercentage': 0.8,
'attr': {
'stroke': '#EDE910', // translates to SVG stroke
'opacity': 1,
'stroke-width': 7 // translates to SVG stroke-width
}
},
'critical': {
'show': true,
'fromPercentage': 0.8,
'toPercentage': 1,
'attr': {
'stroke': '#EF0E0E', // translates to SVG stroke
'opacity': 1,
'stroke-width': 7 // translates to SVG stroke-width
}
}
},
'hand': {
'centerPointRadius': 4,
'length': 65,
'attr': {
'fill': '#333'
}
},
// animation
'duration': 700, // total duration of the entire animated sequence
'easing': 'easeOut', // Easing effect when gauge hand animates
/**
* This functions fires on each frame of the gauge meter animation
* The scope of this function is the HTML container of the SVGGauge widget instance
*
* @param data All real-time information based on the current angle of the gauge hand.
* 'data' has the following properties:
* data.value
* data.percentage e.g. 0.834 (where gauge.percentagePrecision is 1)
* data.percentageString e.g. 83.4% (where gauge.percentagePrecision is 1)
* data.angle The angle relative to gauge.startingIncline
* data.totalAngle gauge.endingIncline - gauge.startingIncline
* data.zone The zone as Integer (1=normal, 2=warning, 3=critical)
* @param gauge The SVGGauge instance
*/
'onAnimate': function(data, gauge) {},
/**
* This function fires at the moment the gauge meter crosses into another zone
* The scope of this function is the HTML container of the SVGGauge widget instance
*
* @param zone The zone as string. Possible values: 'normal', warning', 'critical'.
* @param data All real-time information based on the current angle of the gauge hand.
* 'data' has the following properties:
* data.value
* data.percentage e.g. 0.834 (where gauge.percentagePrecision is 1)
* data.percentageString e.g. 83.4% (where gauge.percentagePrecision is 1)
* data.angle The angle relative to gauge.startingIncline
* data.totalAngle gauge.endingIncline - gauge.startingIncline
* data.zone The zone as Integer (1=normal, 2=warning, 3=critical)
* data.previousZone The zone as Integer (1=normal, 2=warning, 3=critical)
* @param gauge The SVGGauge instance
*/
'onZoneChange': function(zone, data, gauge) {}
};
this.sets = {}; // reserved object for holding different types of Raphael Sets
}
if (msg !== undefined) {
return (window.console && console.warn(msg)) || alert(msg);
}
return true;
},
/**
* This function creates the Raphael paper
*/
create: function() {
this.paper = Raphael(this.container, this.width, this.height);
$.extend(true, this.paper, RaphaelExtensions);
try {
delete window.RaphaelExtensions;
} catch (e) {
window['RaphaelExtensions'] = undefined;
}
this.paper.HTMLcontainer = this.container; // add a reference of the SVG document's HTML container to the paper
},
/**
* This is a triage function which draws the gauge components
*/
draw: function() {
//this.paper.circle(b.center.x, b.center.y, this.radius);
this._drawGaugeZones();
this._drawGaugeTicks();
this._drawHand();
},
_drawHand: function() {
var b = this.bbox,
dotRadius = this.hand.centerPointRadius,
rx = dotRadius, // for clarity
ry = dotRadius, // for clarity
handAttr = $.extend({}, this.hand.attr, {
'stroke-width': 0 // until future versions of this plugin draws the hand in a less sloppy way, we won't have stroke-widths
}),
dot = this.paper.ellipse(b.center.x, b.center.y, rx, ry)
.attr(handAttr),
handLength = this.hand.length,
hand = this.paper.path([
['M', [b.center.x, b.center.y - ry].join(',')],
['L', [b.center.x - handLength, b.center.y].join(',')],
['L', [b.center.x, b.center.y + ry].join(',')]
].join(''))
.attr(handAttr),
handSet = this.paper.set();
handSet.push(dot, hand);
handSet.rotate(this.startingIncline, b.center.x, b.center.y);
this.sets.hand = handSet;
},
_drawGaugeZones: function() {
var b = this.bbox,
instance = this,
zoneSet = this.paper.set();
$.each(this.zones, function(key, zone) {
var zonePath = instance.paper.path()
.attr($.extend(true, {}, zone.attr, {
'gaugeArcZone': [
b.center.x,
b.center.y,
zone.fromPercentage,
zone.toPercentage,
instance.radius - zone.attr['stroke-width'] / 2,
instance.startingIncline,
instance.endingIncline
]
}))
.rotate(instance.startingIncline, b.center.x, b.center.x);
if (!zone.show) {
zonePath.hide();
}
zoneSet.push(zonePath);
});
var arcPath = this.paper.path()
.attr($.extend(true, {}, this.arc.attr, {
'gaugeArcZone': [
b.center.x,
b.center.y,
0,
1,
this.radius,
this.startingIncline,
this.endingIncline
]
}))
.rotate(instance.startingIncline, b.center.x, b.center.x);
if (!this.arc.show) {
arcPath.hide();
}
this.sets.arc = arcPath;
$.extend(this.sets, {
'zones': zoneSet
});
},
_drawGaugeTicks: function() {
// draw gauge ticks
var majorTickGuideSet = this.paper.set(),
majorTickSet = this.paper.set(),
minorTickGuideSet = this.paper.set(),
minorTickSet = this.paper.set(),
iMajorTicks = this.majorTicks.count,
iMinorTicks = this.minorTicks.count,
majorTickAngleIncrement = this.totalAngle / iMajorTicks,
minorTickAngleIncrement = majorTickAngleIncrement / (iMinorTicks + 1),
b = this.bbox,
iTotalLength;
for (var i = 0,
k = 1,
j = iMajorTicks,
a1 = this.startingIncline; i <= j; a1 = this.startingIncline + k * majorTickAngleIncrement, i++, k++) {
var majorTickGuide = this.paper.path([
'M', [b.center.x, b.center.y].join(','),
'L', [b.left.x, b.left.y].join(',')
].join(''))
.attr({
'fill-opacity': 0,
'stroke-opacity': 0,
'stroke-width': 0
})
.rotate(a1, b.center.x, b.center.y);
iTotalLength = majorTickGuide.getTotalLength();
var majorTick = this.paper.path(
majorTickGuide.getSubpath(iTotalLength - this.majorTicks.length, iTotalLength)
)
.rotate(a1, b.center.x, b.center.y)
.attr(this.majorTicks.attr);
if (i < j) {
for (var a2 = a1 + minorTickAngleIncrement,
l = 0,
m = 1,
n = iMinorTicks; l <= n; a2 = a1 + m * minorTickAngleIncrement, l++, m++) {
var minorTickGuide = this.paper.path([
'M', [b.center.x, b.center.y].join(','),
'L', [b.left.x, b.left.y].join(',')
].join(''))
.attr({
'fill-opacity': 0,
'stroke-opacity': 0,
'stroke-width': 0
})
.rotate(a1, b.center.x, b.center.y);
iTotalLength = minorTickGuide.getTotalLength();
var minorTick = this.paper.path(
minorTickGuide.getSubpath(iTotalLength - this.minorTicks.length, iTotalLength)
)
.rotate(a2, b.center.x, b.center.y)
.attr(this.minorTicks.attr);
minorTickGuideSet.push(minorTickGuide);
minorTickSet.push(minorTick);
}
}
majorTickGuideSet.push(majorTickGuide);
majorTickSet.push(majorTick);
}
if (!this.majorTicks.show) {
majorTickSet.hide();
}
if (!this.minorTicks.show) {
minorTickSet.hide();
}
$.extend(this.sets, {
'majorTicks': {
'guides': majorTickGuideSet,
'ticks': majorTickSet
},
'minorTicks': {
'guides': minorTickGuideSet,
'ticks': minorTickSet
}
});
},
_validateOptions: function() {
var w = this.width - this.margin * 2,
h = this.height - this.margin * 2,
cx = this.width / 2,
cy = this.height / 2,
b = geom.getBoundingBox(w, h, cx, cy);
this.bbox = b; // bounding box information
this.radius = this.width / 2 - this.margin; // this.radius is a derived property
// ensure startingIncline is within 2nd and 3rd quadrant
if (!$.isNumeric(this.startingIncline)) {
this.startingIncline = 0;
} else {
this.startingIncline = Math.max(-89, Math.min(89, this.startingIncline));
}
// ensure endingIncline is within 1st and 4th quadrant
if (!$.isNumeric(this.endingIncline)) {
this.endingIncline = 180 + -this.startingIncline; // symmetrical by default
} else {
this.endingIncline = Math.max(91, Math.min(269, this.endingIncline));
}
this.totalAngle = this.endingIncline - this.startingIncline;
},
_getBox: function() {
var w = this.width,
h = this.height;
return {
}
},
/**
* This function sets or gets the value of the gauge
* @param value
* @return {*} Returns the value of gauge is no arguments provided. Returns true upon successful assignment, false otherwise.
*/
val: function(value) {
// getter
if (value === undefined) {
return this.value;
}
// skip setter if there no change in value
if (value == this.value) {
return true;
}
// must be evaluated as numeric
if (!$.isNumeric(value)) {
return false;
}
// F.T.I.
value = value * 1; // ensure it is numeric
// setter
// make sure 'value' is within the allowed range
value = Math.max(Math.min(value.toFixed(this.valuePrecision), this.maxValue), this.minValue);
//console.warn(this.minValue);
var b = this.bbox,
finalPercentage = (value - this.minValue) / Math.abs(this.maxValue - this.minValue),
totalAngle = Math.abs(this.endingIncline - this.startingIncline),
finalAngle = totalAngle * finalPercentage + this.startingIncline,
instance = this,
handSet = this.sets.hand,
previousZone = this.zone;
function onAnimate(obj) {
var data = instance._getRealtimeValues();
if ($.isFunction(instance.onAnimate)) {
instance.onAnimate.apply(instance.container, [data, instance]);
}
if (previousZone != data.zone) {
if ($.isFunction(instance.onZoneChange)) {
data.previousZone = previousZone;
instance.onZoneChange.apply(instance.container, [ZONES[data.zone], data, instance]);
}
previousZone = data.zone;
}
}
eve.on('raphael.anim.frame.' + handSet[1].id, onAnimate);
handSet.animate({
transform: ['r', finalAngle, b.center.x, b.center.y]
}, this.duration, this.easing, function() {
eve.unbind('raphael.anim.frame.' + handSet[1].id, onAnimate);
});
// ...
this.value = value;
return true;
},
_getRealtimeValues: function() {
var hand = this.sets.hand[0],
currentRotation = hand.attr('transform')[0][1] * 1,
currentAngle = currentRotation - this.startingIncline,
totalAngle = Math.abs(this.endingIncline - this.startingIncline),
currentPercentage = currentAngle / totalAngle,
currentValue = Math.abs(this.maxValue - this.minValue) * currentPercentage + this.minValue,
currentZone = (this.zones.normal.fromPercentage <= currentPercentage && currentPercentage < this.zones.normal.toPercentage) ? NORMAL : (this.zones.warning.fromPercentage <= currentPercentage && currentPercentage < this.zones.warning.toPercentage) ? WARNING : (this.zones.critical.fromPercentage <= currentPercentage && currentPercentage < this.zones.critical.toPercentage) ? CRITICAL : 0; // unknown
// for display purposes
currentPercentage = currentPercentage.toFixed(this.percentagePrecision + 2) * 1; // e.g. 0.08543 = 85.43%
currentValue = currentValue.toFixed(this.valuePrecision) * 1;
return {
'value': currentValue,
'percentage': currentPercentage,
'percentageString': (currentPercentage * 100).toFixed(this.percentagePrecision) + '%',
'angle': currentAngle,
'zone': currentZone,
'totalAngle': totalAngle
};
},
/**
* For demo purposes
* @return {*}
*/
randomize: function() {
var iMin = this.minValue,
iMax = this.maxValue,
iRand = Math.floor(Math.random() * (iMax - iMin + 1) + iMin);
this.val(iRand);
return this;
}
};
})(jQuery, Raphael, window.RaphaelExtensions);
/**
* Common styles -- not required
*/
html, body
{
font-family:Consolas;
font-size:10pt;
background-color:#222;
color:#bec8cb;
border:0;
margin:0;
padding:0;
}
a, a:visited {
color:cyan;
}
br.clear
{
clear: both;
}
div.code
{
background-color:#293730;
color:white;
padding:3px;
border:2px solid #50534f;
font-size:10pt;
min-height: 150px;
position:absolute;
overflow:auto;
width:480px;
height:320px;
z-index:9999;
border-radius:10px;
display:none;
opacity:0.95;
}
div.demo-close {
position:absolute;
right:5px;
top:5px;
background-image:url('data:text/javascript;base64,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');
background-repeat: no-repeat;
background-position:top right;
cursor:pointer;
display:block;
width:36px;
height:36px;
z-index:10000;
color:#005d01;
}
div.demo
{
border-top:1px inset #454545;
padding: 8px;
/*width:100%;*/
min-height: 220px;
}
/************************ EXAMPLE 1 ************************/
/* default speedometer face style */
div.default-gauge {
width:150px;
height:160px;
border:1px solid #232;
border-radius:10px;
overflow:hidden;
background-color:#121;
cursor:pointer;
}
div.metal-border {
float:left;
margin-right:10px;
width:153px;
height:164px;
padding:5px;
cursor:pointer;
position:relative;
background: -moz-linear-gradient(
top,
#ffffff 0%,
#ebebeb 50%,
#dbdbdb 50%,
#b5b5b5);
background: -webkit-gradient(
linear, left top, left bottom,
from(#ffffff),
color-stop(0.50, #ebebeb),
color-stop(0.50, #dbdbdb),
to(#b5b5b5));
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 15px;
border: 1px solid #949494;
-moz-box-shadow:
0px 1px 3px rgba(000,000,000,0.5),
inset 0px 0px 2px rgba(255,255,255,1);
-webkit-box-shadow:
0px 1px 3px rgba(000,000,000,0.5),
inset 0px 0px 2px rgba(255,255,255,1);
box-shadow:
0px 1px 3px rgba(000,000,000,0.5),
inset 0px 0px 2px rgba(255,255,255,1);
text-shadow:
0px -1px 0px rgba(000,000,000,0.2),
0px 1px 0px rgba(255,255,255,1);
}
/* fan-shaped speedometer */
div.speedometer-half,
div.metal-border.speedometer-half div.example {
height:90px;
}
div.metal-border.speedometer-half
{
height:95px;
}
div.speedometer-3quarters,
div.metal-border.speedometer-3quarters div.example {
height:150px;
}
div.metal-border.speedometer-3quarters
{
height:155px;
}
/* white speedometer */
div.metal-border div.example {
border:1px solid #232;
border-radius:10px;
background-color: #ffffff;
background: -moz-linear-gradient(
top,
#878787 0%,
#dedede 50%,
#dedede 50%,
#adaaad);
background: -webkit-gradient(
linear, left top, left bottom,
from(#878787),
color-stop(0.50, #dedede),
color-stop(0.50, #dedede),
to(#adaaad));
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
border: 1px solid #949494;
-moz-box-shadow:
0px 1px 0px rgba(000,000,000,0.5),
inset 1px 1px 3px rgba(13,13,13,1);
-webkit-box-shadow:
0px 1px 0px rgba(000,000,000,0.5),
inset 1px 1px 3px rgba(13,13,13,1);
box-shadow:
0px 1px 0px rgba(000,000,000,0.5),
inset 1px 1px 3px rgba(13,13,13,1);
text-shadow:
0px -1px 0px rgba(000,000,000,0.2),
0px 1px 0px rgba(255,255,255,1);
}
div.gauge-value,
div.gauge-percentage,
div.gauge-status {
position:absolute;
}
#example1B div.gauge-value {
top:72px;
right:15px;
width:75px;
text-align:right;
font-size:10pt;
color:#000;
}
#example1B div.gauge-percentage {
top:35px;
width:150px;
font-size:32pt;
font-weight:bold;
color:#000;
text-align:center;
opacity:0.2;
}
#example1D div.gauge-value {
top:60px;
right:15px;
width:75px;
font-size:22pt;
color:#000;
text-align:right;
}
#example1E div.gauge-status {
top:120px;
left:38px;
width:80px;
padding:5px 3px 2px 3px;
font-size:9pt;
text-align:center;
vertical-align:bottom;
border-radius:8px;
-moz-box-shadow:
0px 1px 0px rgba(000,000,000,0.5),
inset 1px 1px 3px rgba(13,13,13,1);
-webkit-box-shadow:
0px 1px 0px rgba(000,000,000,0.5),
inset 1px 1px 3px rgba(13,13,13,1);
box-shadow:
0px 1px 0px rgba(000,000,000,0.5),
inset 1px 1px 3px rgba(13,13,13,1);
}
#example1E div.gauge-status.normal,
#example1E div.gauge-overlay.normal {
background-color:#009b0b;
color:#aef9b6;
text-shadow:none;
}
#example1E div.gauge-status.warning,
#example1E div.gauge-overlay.warning {
background-color:#efeb00;
color:#81610b;
text-shadow:none;
}
#example1E div.gauge-status.critical,
#example1E div.gauge-overlay.critical {
background-color:#c60217;
color:#f9c9d5;
text-shadow:none;
}
#example1E div.gauge-overlay {
opacity:0.25;
border-radius:70px;
width:122px;
height:122px;
position:absolute;
top:20px;
left:20px;
}
#example1E div.gauge-percentage {
top:90px;
width:150px;
font-size:12pt;
text-align:center;
color:#565656;
opacity:0.5;
}
#example1G div.gauge-value {
top:110px;
right:20px;
width:120px;
font-size:16pt;
color:#000;
text-align:right;
}
div.demo div.example {
float:left;
margin-right:10px;
}
div.demo div.code {
font-family: Consolas;
font-size:9pt;
margin-right:10px;
padding-left:5px;
background-color:#292929;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment