Click here to Skip to main content
12,396,461 members (69,916 online)
Click here to Skip to main content
Add your own
alternative version

Stats

38.9K views
2.1K downloads
35 bookmarked
Posted

A Universal Gauge for Your Web Dashboard

, 16 Dec 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
JavaScript plugin gaugeSVG to generate widely configurable SVG gauge for a web dashboard

Introduction

I was searching the web for a free JavaScript gauge plugin to use it in a web dashboard 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 dosen'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 empty string to be suppressed. Default is an empty 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 empty string to be suppressed. Default is empty 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 from 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. Default 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. Default is "#000000".
  • gaugeShadowScale: [float] The width of the gauge arc's shadow. Default is 1.0. Meaningful values range from 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&sup3;/h (passage)",
            labelColor: "#8888cc",
            gaugeWidthScale: 1.25,
            gaugeBorderColor: "#222244",
            gaugeBorderWidth: 1.5,
            gaugeShadowColor: "#444488",
            gaugeShadowScale: 1.35,
            canvasBackColor: "#f8eeff",
            gaugeBackColor: "#ccccff",
            needleColor: "#8888cc",
            lowerActionLimit: 0,  // Use '0' to disable. A '-1' is obsolete with version 2.0.
            lowerWarningLimit: 0, // Use '0' to disable. A '-1' is obsolete with version 2.0.
            upperWarningLimit: 0, // Use '0' to disable. A '-1' is obsolete with version 2.0.
            upperActionLimit: 0,  // Use '0' to disable. A '-1' is obsolete with version 2.0.
        });
    };
</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 achieve 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 achieve, 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 easiest 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 dependent 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

  • The first version of the article is from June 18th 2013.
  • The second version from December 15th 2014 updates the JavaScript source to support negative display ranges, e.g. from min: -80 to max: -20 with lowerActionLimit: -50 and upperActionLimit: -30.
  • A patch for the second version from October 6th 2015 updates the JavaScript source to fix problems with display ranges from a negative to a positive value and with disabled lower warning limits.

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.

You may also be interested in...

Comments and Discussions

 
QuestionNeedle move to Required Position Pin
Member 1209424328-Oct-15 2:36
memberMember 1209424328-Oct-15 2:36 
AnswerRe: Needle move to Required Position Pin
Steffen Ploetz30-Oct-15 7:04
professionalSteffen Ploetz30-Oct-15 7:04 
QuestionLowerActionLimit Pin
genesxse26-Sep-15 7:40
membergenesxse26-Sep-15 7:40 
AnswerRe: LowerActionLimit Pin
Steffen Ploetz5-Oct-15 18:40
professionalSteffen Ploetz5-Oct-15 18:40 
AnswerRe: LowerActionLimit Pin
Steffen Ploetz6-Oct-15 22:35
professionalSteffen Ploetz6-Oct-15 22:35 
QuestionSmaller gauges? Pin
Member 1169755518-May-15 4:31
memberMember 1169755518-May-15 4:31 
AnswerRe: Smaller gauges? Pin
Member 1169755519-May-15 6:47
memberMember 1169755519-May-15 6:47 
GeneralMy vote of 5 Pin
JNVoirol4-Mar-15 21:17
memberJNVoirol4-Mar-15 21:17 
QuestionHow to bind it with a data set C# Pin
write2varun25-Dec-14 19:33
memberwrite2varun25-Dec-14 19:33 
QuestionRe: How to bind it with a data set C# Pin
write2varun23-Feb-15 20:38
memberwrite2varun23-Feb-15 20:38 
AnswerRe: How to bind it with a data set C# Pin
Steffen Ploetz4-Mar-15 22:04
professionalSteffen Ploetz4-Mar-15 22:04 
AnswerRe: How to bind it with a data set C# Pin
Steffen Ploetz4-Mar-15 21:48
professionalSteffen Ploetz4-Mar-15 21:48 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun16-Dec-14 0:56
memberHumayun Kabir Mamun16-Dec-14 0:56 
Questionrunning from web server Pin
Member 1124049617-Nov-14 3:46
memberMember 1124049617-Nov-14 3:46 
AnswerRe: running from web server Pin
Steffen Ploetz15-Dec-14 9:03
professionalSteffen Ploetz15-Dec-14 9:03 
QuestionAnimation and color Pin
Member 1047641025-Sep-14 4:00
memberMember 1047641025-Sep-14 4:00 
AnswerRe: Animation and color Pin
Steffen Ploetz15-Dec-14 8:40
professionalSteffen Ploetz15-Dec-14 8:40 
QuestionNeedle Attributes Pin
Member 1027340516-Sep-13 20:21
memberMember 1027340516-Sep-13 20:21 
AnswerRe: Needle Attributes Pin
Steffen Ploetz17-Sep-13 9:38
professionalSteffen Ploetz17-Sep-13 9:38 
GeneralRe: Needle Attributes Pin
Member 1027340517-Sep-13 18:51
memberMember 1027340517-Sep-13 18:51 
GeneralRe: Needle Attributes Pin
Steffen Ploetz17-Sep-13 21:13
professionalSteffen Ploetz17-Sep-13 21:13 
GeneralRe: Needle Attributes Pin
natasha_00917-Sep-13 21:22
membernatasha_00917-Sep-13 21:22 
GeneralRe: Needle Attributes Pin
Steffen Ploetz18-Sep-13 2:36
professionalSteffen Ploetz18-Sep-13 2:36 
GeneralRe: Needle Attributes Pin
natasha_00918-Sep-13 21:53
membernatasha_00918-Sep-13 21:53 
QuestionNot working in IE-8 Pin
Birjesh Yadav15-Sep-13 19:23
memberBirjesh Yadav15-Sep-13 19:23 
AnswerRe: Not working in IE-8 Pin
Steffen Ploetz17-Sep-13 9:17
professionalSteffen Ploetz17-Sep-13 9:17 
GeneralRe: Not working in IE-8 Pin
MrFlash081524-Mar-14 12:39
memberMrFlash081524-Mar-14 12:39 
GeneralRe: Not working in IE-8 Pin
Steffen Ploetz25-Mar-14 3:28
professionalSteffen Ploetz25-Mar-14 3:28 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun20-Jun-13 0:02
memberHumayun Kabir Mamun20-Jun-13 0:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    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 | Terms of Use | Mobile
Web01 | 2.8.160721.1 | Last Updated 16 Dec 2014
Article Copyright 2013 by Steffen Ploetz
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid