/** * FlexGauge * Version: 1.0 * Author: Jeff Millies * Author URI: */ (function ($) { var FlexGauge = function (o) { if (typeof o === 'object') { this._extendOptions(o, false); this._build(); } }; FlexGauge.prototype = { /** * {String} Element that you would like to append to. ie '#idname', '.classname', 'div#idname', etc.. */ appendTo: 'body', /** * {String} Id of Canvas already created or Id of canvas that will be created automatically */ elementId: 'canvas', /** * {String} Class of canvas created */ elementClass: 'canvas', /** * {Int} Canvas Width & Height */ elementWidth: 200, elementHeight: 200, /** * {Boolean|String} Generate Dial Value for the Gauge, true will use arcFillPercent or arcFillInt * depending on provided values and specified dialUnits, string will use specified value */ dialValue: false, /** * {String} Class applied to div when dial is generated. */ dialClass: 'fg-dial', /** * {string: %|$| } Type of unit to use for the dial */ dialUnit: '%', /** * {string: before|after} Where the dial unit will be displayed */ dialUnitPosition: 'after', /** * {Boolean|String} Generate Label for the Gauge, true will use default "FlexGauge", string will use specified */ dialLabel: false, /** * {String} Class applied to div when label is generated. */ dialLabelClass: 'fg-dial-label', /** * {Int} Radius of the arc */ inc: 0.0, incTot: 1.0, /** * {Doule} Increment value */ arcSize: 85, /** * {double} Starting and Ending location of the arc, End always needs to be larger * arc(x, y, radius, startAngle, endAngle, anticlockwise) */ arcAngleStart: 0.85, arcAngleEnd: 2.15, /** * {double} Percentage the arc fills */ arcFillPercent: .5, /** * {Int} Starting and Ending values that are used to * find a difference for amount of units * ie: 60 (arcFillEnd) - 10 (arcFillStart) = 50 */ arcFillStart: null, arcFillEnd: null, /** * {Int} Data used to find out what percentage of the * arc to fill. arcFillInt can be populated by * the difference of arcFillStart and arcFillEnd */ arcFillInt: null, arcFillTotal: null, /** * {Int} Color lightness: 0 - 255, 0 having no white added, 255 having all white and no color */ arcBgColorLight: 80, /** * {Int} Color saturation: 0 - 100, 0 having no color, 100 is full color */ arcBgColorSat: 60, /** * {Int} Size of the line marking the percentage */ arcStrokeFg: 30, /** * {Int} Size of the container holding the line */ arcStrokeBg: 30, /** * {string: hex} Color of the line marking the percentage */ colorArcFg: '#5bc0de', /** * {string: hex} Color of the container holding the line, default is using the Fg color and lightening it */ colorArcBg: null, /** * {String} Instead of providing a color or hex for the color, you can provide a class from the style * sheet and specify what you would like to grab for the color in styleSrc */ styleArcFg: null, styleArcBg: null, styleSrc: 'color', /** * {Boolean} If set to false, then the graph will not be animated */ animateEasing: true, /** * {Int} Speed for the animation, 1 is fastest, higher the number, slower the animation */ animateSpeed: 5, /** * {Int} Math used in animation speed */ animateNumerator: 12, animateDivisor: 15, /** * {double} Placeholder for current percentage while animating */ _animatePerc: 0.00, /** * {Object} Placeholder for setInterval */ _animateLoop: null, /** * {Object} Placeholder for canvas */ _canvas: null, /** * {Object} Placeholder for canvas context */ _ctx: null, update: function (o) { if (typeof o === 'object') { var difference; // if using int, convert to percent to check difference if (typeof o.arcFillInt !== 'undefined' && o.arcFillInt == this.arcFillInt && typeof o.arcFillTotal !== 'undefined' && o.arcFillTotal == this.arcFillTotal) { o.arcFillPercent = this.arcFillPercent; } else if (typeof o.arcFillInt !== 'undefined' && typeof o.arcFillTotal !== 'undefined' && (o.arcFillInt != this.arcFillInt || o.arcFillTotal == this.arcFillTotal)) { o.arcFillPercent = (o.arcFillInt / o.arcFillTotal); } else if (typeof o.arcFillInt !== 'undefined' && typeof o.arcFillTotal === 'undefined' && (o.arcFillInt != this.arcFillInt)) { o.arcFillPercent = (o.arcFillInt / this.arcFillTotal); } if (typeof o.arcFillPercent !== 'undefined') { difference = Math.abs((this.arcFillPercent - o.arcFillPercent)); } else { difference = this.arcFillPercent; } this._extendOptions(o, true); clearInterval(this._animateLoop); if (difference > 0) { var that = this; this._animateLoop = setInterval(function () { return that._animate(); }, (this.animateSpeed * this.animateNumerator) / (difference * this.animateDivisor)); } } }, _extendOptions: function (o, update) { var color = false; if (update) color = this.colorArcFg; $.extend(this, o, true); if (typeof o.arcFillStart !== 'undefined' && typeof o.arcFillEnd !== 'undefined' && typeof o.arcFillTotal !== 'undefined') { this.arcFillInt = (o.arcFillEnd - o.arcFillStart); } if (typeof o.arcFillPercent === 'undefined' && this.arcFillInt !== null && this.arcFillInt >= 0 && this.arcFillTotal !== null && this.arcFillTotal > 0) { this.arcFillPercent = this.arcFillInt / this.arcFillTotal; } if (typeof o.elementId === 'undefined') { this.elementId = 'fg-' + this.appendTo + '-canvas'; } // supporting color if pass, changing to hex if (typeof o.colorArcFg !== 'undefined') { this.colorArcFg = colorToHex(o.colorArcFg); } if (typeof o.colorArcBg !== 'undefined') { this.colorArcBg = colorToHex(o.colorArcBg); } // only use the styleArcFg if colorArcFg wasn't specified in the options if (typeof o.styleArcFg !== 'undefined' && typeof o.colorArcFg === 'undefined') { this.colorArcFg = getStyleRuleValue(this.styleSrc, this.styleArcFg); } if (typeof o.colorArcBg === 'undefined' && this.colorArcBg === null && this.colorArcFg !== null) { this.colorArcBg = this.colorArcFg; } if (typeof this.colorArcBg !== null && (!update || colorToHex(this.colorArcFg) != colorToHex(color))) { if (colorToHex(this.colorArcFg) != colorToHex(color)) this.colorArcBg = this.colorArcFg; this.colorArcBg = shadeColor(this.colorArcBg, this.arcBgColorLight, this.arcBgColorSat); } if (typeof o.dialLabel === 'boolean' && o.dialLabel) { this.dialLabel = 'FlexGauge'; } }, _build: function () { if (document.getElementById(this.elementId) === null) { $(this.appendTo).append(''); } this._canvas = document.getElementById(this.elementId); this._ctx = this._canvas.getContext("2d"); this.arcAngleStart = this.arcAngleStart * Math.PI; this.arcAngleEnd = this.arcAngleEnd * Math.PI; if (this.animateEasing === false) { this._animatePerc = this.arcFillPercent; } var that = this; this._animateLoop = setInterval(function () { return that._animate(); }, (this.animateSpeed * this.animateNumerator) / (this.arcFillPercent * this.animateDivisor)); }, _animate: function () { var animateInt = Math.round(this._animatePerc * 100); var arcInt = Math.round(this.arcFillPercent * 100); if (animateInt < arcInt) animateInt++; else animateInt--; this._animatePerc = (animateInt / 100); if (animateInt === arcInt) { this.arcFillPercent = this._animatePerc; clearInterval(this._animateLoop); this._draw(); } this._draw(); }, _draw: function () { //Clear the canvas everytime a chart is drawn this._ctx.clearRect(0, 0, this.elementWidth, this.elementHeight); //Background 360 degree arc this._ctx.beginPath(); this._ctx.strokeStyle = this.colorArcBg; this._ctx.lineWidth = this.arcStrokeBg; this._ctx.arc( this.elementWidth / 2, this.elementHeight / 2 + 50, this.arcSize, 0, Math.PI, true ); this._ctx.stroke(); //var newEnd = ((this.arcAngleEnd - this.arcAngleStart) * this._animatePerc) + this.arcAngleStart; var newStart; var newEnd; var incArc = this.inc*Math.PI/2; if (this.inc >= 0.0){ newStart = -Math.PI/2; newEnd = newStart + incArc; } else { newStart = -Math.PI/2 + incArc; newEnd = -Math.PI/2; } var colorShadesTabRed = ['#ff0000','#ff4000','#ff8000','#ff9900','#ffbf00','#ffff00']; var colorShadesTabGreen = ['#ffff00','#E0FF00','#D0FF00','#a0ff00','#00ff00','#00ff40',]; var colorValue = parseInt(Math.abs((this.inc / this.incTot) * 5)); var theColor; if (this.inc >= 0.0) theColor = colorShadesTabGreen[colorValue]; else theColor = colorShadesTabRed[5-colorValue]; this.colorArcFg = theColor; this._ctx.beginPath(); this._ctx.strokeStyle = this.colorArcFg; this._ctx.lineWidth = this.arcStrokeFg; this._ctx.arc( this.elementWidth / 2, this.elementHeight / 2 + 50, this.arcSize, newStart, newEnd, false ); this._ctx.stroke(); this._renderLabel(); }, _renderLabel: function () { if (this.dialValue) { var dialVal; var dial = $(this.appendTo).find('div.' + this.dialClass); if (dial.length === 0) { $(this.appendTo).append('
'); } dial = $(this.appendTo).find('div.' + this.dialClass); if (typeof this.dialValue === 'boolean') { switch (this.dialUnit) { case '%': dialVal = Math.round(this._animatePerc * 100); break; default: dialVal = Math.round(this.arcFillInt * (this._animatePerc / this.arcFillPercent)); break; } dialVal = (isNaN(dialVal) ? 0 : dialVal); switch (this.dialUnitPosition) { case 'before': dialVal = this.dialUnit + dialVal; break; case 'after': dialVal = dialVal + this.dialUnit; break; } } else { dialVal = this.dialValue; } dial.html(dialVal) } if (this.dialLabel) { var label = $(this.appendTo).find('div.' + this.dialLabelClass); if (label.length === 0) { $(this.appendTo).append(''); } label = $(this.appendTo).find('div.' + this.dialLabelClass); label.html(this.dialLabel); } } }; function shadeColor(col, amt, sat) { if (col[0] == "#") { col = col.slice(1); } var num = parseInt(col, 16); var r = (num >> 16) + amt; if (r > 255) r = 255; else if (r < 0) r = 0; var b = ((num >> 8) & 0x00FF) + amt; if (b > 255) b = 255; else if (b < 0) b = 0; var g = (num & 0x0000FF) + amt; if (g > 255) g = 255; else if (g < 0) g = 0; var gray = r * 0.3086 + g * 0.6094 + b * 0.0820; sat = (sat / 100); r = Math.round(r * sat + gray * (1 - sat)); g = Math.round(g * sat + gray * (1 - sat)); b = Math.round(b * sat + gray * (1 - sat)); return "#" + (g | (b << 8) | (r << 16)).toString(16); } function getStyleRuleValue(style, selector) { $('body').append(''); var element = $('#getStyleRuleValue-' + selector); element.addClass(selector); var color = element.css(style); var hex = colorToHex(color); element.remove(); return hex; } function colorToHex(color) { if (color[0] != 'r') return color; var rgb = color.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); return "#" + ("0" + parseInt(rgb[1], 10).toString(16)).slice(-2) + ("0" + parseInt(rgb[2], 10).toString(16)).slice(-2) + ("0" + parseInt(rgb[3], 10).toString(16)).slice(-2); } if (typeof define === 'function') { define('flex-gauge', ['jquery'], function ($) { return FlexGauge; }); } else { window.FlexGauge = FlexGauge; } })(jQuery);