Click here to Skip to main content
Click here to Skip to main content

A universal gauge for your web dashboard

, 17 Jun 2013
Rate this:
Please Sign up or sign in to vote.
JavaScript plugin gaugeSVG to generate widely configurable SVG gauge for a web dashboard

Download source and sample

Introduction 

I was searching the web for a free JavaScript gauge plugin to use it in a web dashborard included in Ploetz + Zeller GmbH's business process management software Symbio. The best match i found was the very good justGauge, but it has two characteristics that don't fit for me:

  • It is based on the Raphaël JavaScript graphics library and i don't want to have another dependency in my web dashborard. 
  • It don't show warning limits or action limits, that are required for a quality control chart. 

Thus the idea originated to advance the approach of justGauge and to create my own plugin gaugeSVG.

This image shows how gaugeSVG could look like. It is taken from the downloadable source and sample.

Background 

The gaugeSVG is pure SVG, resolution independent vector drawing and it works in almost any browser - IE6+, Chrome, Firefox, Safari, Opera, Android, etc.

Because I will explain the used SVG technologies, this is also a good introduction to high level SVG functionality in connection with JavaScript. 

Using the code

To use gaugeSVG only three requirements are to meet:
  1. The gaugeSVG JavaScript must be included into the html document.
    <script src="javascript/gaugeSVG.js"></script>
    

  2. The html document must contain a  div element with id, width and height that can be used as container for gaugeSVG.
    <div id="container1" style="width:350px; height:300px"></div> 
    
  3. The gaugeSVG JavaScript must be initialized with the id of the html document's div container.
    <script>
        window.onload = function(){
            var gauge1 = new GaugeSVG({
    			id: "container1"
            });
        };
    </script> 
    

The initialization code above shows the minimum initialization. There are much more parameters that can be used to manipulate the gauge functionality and appearance.

This image illustrates the parts of gaugeSVG, most of them are configurable.

The possible initialization parameters are:

  • title: [string] The title text that is displayed above the gauge. It can be an emty string to be suppressed. Default is an emty string.
  • titleColor: [#rrggbb] The title color. Default is "#888888".
  • value: [float] The value to display.  Default is (max - min) / 2.0.  Values below min are shown as min. Values above max are shown as max.
  • valueColor: [#rrggbb] The value text color. Default is "#000000". The accurate value is shown in the center of the gauge as text.
  • label: [string] The label displayed below the value text. It can be an emty string to be suppressed. Default is emty string. Typically used to display the value's measuring unit.
  • labelColor: [#rrggbb] The label text color. Default is "#888888". 
  • min: [float] The minimum of the gauge display range. Will be displayed as text at the gauge start point, if is showMinMax true.
  • max: [float] The maximum of the gauge display range. Will be displayed as text at the gauge start point, if is showMinMax true. 
  • showMinMax: [bool] Hide or display the min and max gauge display range values as text. Default is true. 
  • minmaxColor: [#rrggbb] The min and max value's text color. Default is "#888888". 
  • canvasBackColor: [#rrggbb] The background color of the gauge canvas. Default is "#f8f8f8".
  • gaugeWidthScale: [float] The width of the gauge arc. Default is 1.0. Meaningful values range fron 0.15 to 1.5. Lower values show a smaller arc, higher values show a thicker arc.
  • gaugeBorderColor: [float] The gauge arc border color. Default is "#cccccc".
  • gaugeBorderWidth: [#rrggbb] The gauge arc border width. Default is 0.
  • gaugeBackColor: [#rrggbb] The gauge arc background color. Default is "#cccccc".
  • showGaugeShadow: [bool] Hide or display a gauge arc shadow. Dafault is true. The gauge shadow is made of a SVG radial gradient. The gradient start color is the gaugeShadowColor. The gradient stop color is the gaugeBackColor.
  • gaugeShadowColor: [#rrggbb] The gauge arc shadow color. Dafault is "#000000".
  • gaugeShadowScale: [float] The width of the gauge arc's shadow. Default is 1.0. Meaningful values range fron 0.8 to 1.5. Lower values show a smaller shadow, higher values show a thicker shadow
  • lowerActionLimit: [float] The lower action limit or a negative value, if not desired.  Default is (max - min) * 0.15 + min. 
  • lowerWarningLimit: [float] The lower warning limit or a negative value, if not desired.  Default is (max - min) * 0.30 + min.
  • upperWarningLimit: [float] The upper warning limit or a negative value, if not desired.  Default is (max - min) * 0.70 + min.
  • upperActionLimit: [float] The upper action limit or a negative value, if not desired.  Default is (max - min) * 0.85 + min.
  • needleColor: [#rrggbb] The gauge needle color. Default is "#444444".
  • optimumRangeColor: [#rrggbb] The optimum range color. Default is "#44ff44".
  • warningRangeColor: [#rrggbb] The warning range color. Default is "#ffff00".
  • actionRangeColor: [#rrggbb] The action range color. Default is "#ff4444".

A stronger adjusted initialization code could look like this:

<script>
    window.onload = function(){
        var gauge2 = new GaugeSVG({
			id: "container2", 
			value: 49,
			valueColor: "#444488",
			min: 30,
			max: 70,
			minmaxColor: "#444488",
			title: "Gauge 2",
			titleColor: "#8888cc",
			label: "m³/h (passage)",
			labelColor: "#8888cc",
			gaugeWidthScale: 1.25,
			gaugeBorderColor: "#222244",
			gaugeBorderWidth: 1.5,
			gaugeShadowColor: "#444488",
			gaugeShadowScale: 1.35,
			canvasBackColor: "#f8eeff",
			gaugeBackColor: "#ccccff",
			needleColor: "#8888cc",
			lowerActionLimit: -1,
			lowerWarningLimit: -1,
			upperWarningLimit: -1,
			upperActionLimit: -1,
        });
    };
</script> 

Points of Interest 

SVG injection

One interesting aspect of gaugeSVG development was the dynamic injection of SVG nodes into the DOM using JavaScript. The very first step to achive this was to create the SVG canvas into the DOM's DIV container. 

// Determine drawing container.
var container = document.getElementById(params.id);
 
...
 
// Create SVG canvas.
this.canvas  = document.createElementNS(svgns, "svg");
this.canvas.setAttributeNS(null, 'version', "1.1");
this.canvas.setAttributeNS(null, 'width', "100%");
this.canvas.setAttributeNS(null, 'height', "100%");
this.canvas.setAttributeNS(null, 'style', "overflow: hidden; position: relative;
  left: -0.5px; top: -0.5px;");
container.appendChild(this.canvas);

 The next step was to insert SVG nodes into the SVG canvas, e. g. the background rectangle. 

// Draw canvas background.
this.rectBG = document.createElementNS(svgns, 'rect');
this.rectBG.setAttributeNS(null, 'stroke', "none");
this.rectBG.setAttributeNS(null, 'fill',   this.config.canvasBackColor);
this.rectBG.setAttributeNS(null, 'x',      this.config.offsetX);
this.rectBG.setAttributeNS(null, 'y',      this.config.offsetY);
this.rectBG.setAttributeNS(null, 'width',  this.config.canvasW);
this.rectBG.setAttributeNS(null, 'height', this.config.canvasH);
this.canvas.appendChild(this.rectBG); 

Mind the calls of createElementNS, setAttributeNS and appendChild and take care of the right objects to call the methods on and to pass as parameter. 

SVG text

To draw text into the SVG canvas was a little challenging. This was because of the surprising nesting of a DOM node into a SVG node.

// Draw current value.
this.gaugeVAL = document.createElementNS(svgns, 'text');
this.gaugeVAL.setAttributeNS(null, 'x',      this.config.offsetX + this.config.canvasW / 2.0);
this.gaugeVAL.setAttributeNS(null, 'y',      this.config.offsetY + this.config.canvasH / 1.2);
this.gaugeVAL.setAttributeNS(null, 'style',  "font-family:Arial,Verdana; font-size:" +
        Math.floor(this.config.canvasW / 8) + "px; font-weight:bold; fill-opacity:1.0; fill:" +
        this.config.valueColor + "; text-anchor:middle;");
this.gaugeVAL.appendChild(document.createTextNode(this.config.originalValue));
this.canvas.appendChild(this.gaugeVAL); 

Note that a DOM text node (document.createTextNode(this.config.originalValue)) has to be created as a SVG text nodes child (this.gaugeVAL.appendChild(...);).

Updating

As well the needle position as the value text might be updated during the gauge lifetime/display of the HTML document, e. g.  to show the latest value of a continuous measurement. This can be done by the gaugeSVG's method refresh().  A life update is very easy to achive, because the attributes of DOM nodes and SVG nodes can be manipulated using JavaScript. For the value text, the one and only DOM text child node of the SVG text node has to be set with the new value text. For the needle the 'd' attribute has to be updated with the new path.

this.gaugeVAL.childNodes[0].textContent = this.config.value;
 
this.gaugeNDL.setAttributeNS(null, 'd', this.calculateNeedlePath(...));

This approach is not only the easyest way, but it also recycles the already existing DOM and SVG nodes and avoids flicker.

Animation

The updating of the needle position can be animated, if gaugeSVG.refresh(valueNew, animated) parameter animated is set to true. The animation uses JavaScript's setTimeout. To realize a visually appealing animation, the value this.animation.startIncrementDivisor defines the 24th part of the difference from the last needle position to the new needle position as initial incremental step. Every incremental step will be drawn with a delay this.animation.delay of 15 milliseconds.

To emulate a declining needle speed, the incremental steps decrease by a multiplication with the value this.animation.decreaseOfIncrementValue of 0.966.  To prevent an infinite animation, the total number of incremental steps is limited to the value this.animation.maxIncrements of 48.

These this.animation.* values work fine for updating intervals not smaller than 2 seconds. In case of a faster updating no animation should be done or the values must be adapted. Any adaption needs extensive testing.

Note that any function, called by setTimeout(), will leave the context of the gaugeSVG's this pointer. To provide the first call of the timed out function with the gaugeSVG's this pointer, a explicit reference must be created. All subsequent calls can hand over this reference. 

// At that time, the timed out function will be called, the context,
// where 'this' pointer is known to, is no longer valid. 
var gauge = this;
setTimeout(function()
  {
    GaugeAnimationStep(gauge, oldValue, incrementValue, gauge.animation.maxIncrements);)
  }, gauge.animation.delay);

Shadow / gradient

The gauge background can be drawn with a shadow effect. This is realized with a radial gradient. Any gradient is part of a definition SVG node: document.createElementNS(svgns, 'defs') All definition SVG nodes are transparent through the complete document. Thus, if multiple gauges should be shown within a single document, the gradient definitions must distinguish: setAttributeNS(null, 'id', this.config.id + "_gradient")

// Create gradient.
if (this.config.showGaugeShadow == true)
{
  this.gradients = document.createElementNS(svgns, 'defs');
  this.gradients.setAttributeNS(null, 'id', "gradients");
  this.gradient = document.createElementNS(svgns, 'radialGradient');
  this.gradients.appendChild(this.gradient);
  this.gradient.setAttributeNS(null, 'id', this.config.id + "_gradient");
  this.gradient.setAttributeNS(null, 'cx', "50%");
  this.gradient.setAttributeNS(null, 'cy', "50%");
  this.gradient.setAttributeNS(null, 'r',  "100%");
  this.gradient.setAttributeNS(null, 'fx', "50%");
  this.gradient.setAttributeNS(null, 'fy', "50%");
  this.gradient.setAttributeNS(null, 'gradientTransform', "scale(1 2)");
  this.grad1sub1 = document.createElementNS(svgns, 'stop');
  this.gradient.appendChild(this.grad1sub1);
  this.grad1sub1.setAttributeNS(null, 'offset', "15%");
  this.grad1sub1.setAttributeNS(null, 'style', "stop-color:" +
    this.config.gaugeShadowColor + ";stop-opacity:1");
  this.grad1sub2 = document.createElementNS(svgns, 'stop');
  this.gradient.appendChild(this.grad1sub2);
  this.grad1sub2.setAttributeNS(null, 'offset',
    this.config.gaugeShadowScale * 33 + "%");
  this.grad1sub2.setAttributeNS(null, 'style', "stop-color:" +
    this.config.gaugeBackColor + ";stop-opacity:1");
  this.canvas.appendChild(this.gradients);
}

The radial gradient shall be drawn centered to the path it is applied to, the gauge background path. The center of the outermost color ring is set relative to the path: setAttributeNS(null, 'cx', "50%"), setAttributeNS(null, 'cy', "50%") The innermost color ring is also set relative to the path: setAttributeNS(null, 'fx', "50%"), setAttributeNS(null, 'fy', "50%") Because the path of the gauge background has a width:height ratio of 2:1, the radial gradient would look elliptic. To achive an exactly circular gradient, a scale ratio of 1:2 is applied: setAttributeNS(null, 'gradientTransform', "scale(1 2)") The fade from innermost color to outermost color is distributed nonuniform between gradient center and gradient border. The fade starts at 15% from center to border setAttributeNS(null, 'offset', "15%") and ends dependend from gaugeShadowScale at setAttributeNS(null, 'offset', this.config.gaugeShadowScale * 33 + "%").

After the gradient's definition it can be applied. 

// Draw gauge background.
this.gaugeBG = document.createElementNS(svgns, 'path');
this.gaugeBG.setAttributeNS(null, 'stroke', this.config.gaugeBorderColor);
this.gaugeBG.setAttributeNS(null, 'stroke-width', this.config.gaugeBorderWidth);
if (this.config.showGaugeShadow == true)
{
  this.gaugeBG.setAttributeNS(null, 'fill',   "url(#" + this.config.id + "_gradient)");
}
else
{
  this.gaugeBG.setAttributeNS(null, 'fill',   this.config.gaugeBackColor);
}

 Have fun with gaugeSVG!

History

This is the first version of the article from June 18th 2013.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Steffen Ploetz
CEO Ploetz + Zeller GmbH
Germany Germany
I am CEO at Ploetz + Zeller GmbH, Munich Germany (www.p-und-z.com)
 
Ploetz + Zeller GmbH is a consulting and software services company committed to pro-active and professional governance and optimization of our clients' company processes. Furthermore it offers Symbio (www.symbioworld.com) software, a very powerful and easy to use business process management suite.
 
My responsibilities range from product ownership of Symbio via responsibility for the architecture of some Symbio software components to implementation of software core parts (e. g. automatic layout).
 
I started programming 1986 with C64 BASIC and came via Pascal, Turbo Pascal, C, Turbo C, C++ and Java to C#. I like the potential of C++ very much, but now my favorite language is C#.
 
I believe a lot in free knowledge sharing. I'm author at German Wikipedia and, after 10 years of passive membership, author at CODE PROJECT.

Comments and Discussions

 
QuestionNeedle Attributes PinmemberMember 1027340516-Sep-13 20:21 
AnswerRe: Needle Attributes PinprofessionalSteffen Ploetz17-Sep-13 9:38 
GeneralRe: Needle Attributes PinmemberMember 1027340517-Sep-13 18:51 
GeneralRe: Needle Attributes PinprofessionalSteffen Ploetz17-Sep-13 21:13 
GeneralRe: Needle Attributes Pinmembernatasha_00917-Sep-13 21:22 
GeneralRe: Needle Attributes PinprofessionalSteffen Ploetz18-Sep-13 2:36 
GeneralRe: Needle Attributes Pinmembernatasha_00918-Sep-13 21:53 
QuestionNot working in IE-8 PinmemberBirjesh Yadav15-Sep-13 19:23 
AnswerRe: Not working in IE-8 PinprofessionalSteffen Ploetz17-Sep-13 9:17 
GeneralRe: Not working in IE-8 PinmemberMrFlash081524-Mar-14 12:39 
GeneralRe: Not working in IE-8 PinprofessionalSteffen Ploetz25-Mar-14 3:28 
GeneralMy vote of 5 PinmemberHumayun Kabir Mamun20-Jun-13 0:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 18 Jun 2013
Article Copyright 2013 by Steffen Ploetz
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid