65.9K
CodeProject 正在变化。 阅读更多。
Home

适用于您的 Web 仪表盘的通用仪表盘

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (20投票s)

2013 年 6 月 18 日

CPOL

8分钟阅读

viewsIcon

100283

downloadIcon

4598

JavaScript 插件 gaugeSVG, 用于为 Web 仪表盘生成高度可配置的 SVG 仪表盘。

引言

我一直在网上寻找一款免费的 JavaScript 仪表盘插件,用于 Ploetz + Zeller GmbH 的业务流程管理软件 Symbio 的 Web Dashboard 中。我找到的最匹配的是非常出色的 justGauge,但它有两个特点不适合我:

  • 它基于 Raphaël JavaScript 图形库,我不想在我的 Web Dashboard 中引入另一个依赖项。
  • 它不显示质量控制图所需的警告限或操作限。

因此,我产生了改进 justGauge 的方法并创建自己的插件 gaugeSVG 的想法。

这张图片展示了 gaugeSVG 的外观。它取自可下载的源代码和示例

背景

gaugeSVG 是纯 SVG,分辨率无关的矢量图,几乎可以在所有浏览器中运行 - IE6+、Chrome、Firefox、Safari、Opera、Android 等。

由于我将解释使用的 SVG 技术,因此这也是一个关于高级 SVG 功能与 JavaScript 结合的良好介绍。

Using the Code

要使用 gaugeSVG,只需满足三个要求:

  1. 必须将 gaugeSVG JavaScript 包含在 HTML 文档中。
    <script src="javascript/gaugeSVG.js"></script>
  2. HTML 文档必须包含一个 div 元素,该元素具有 idwidthheight,可用于作为 gaugeSVG 的容器。
    <div id="container1" style="width:350px; height:300px"></div> 
  3. 必须使用 HTML 文档 div 容器的 id 初始化 gaugeSVG JavaScript。
    <script>
        window.onload = function(){
            var gauge1 = new GaugeSVG({
                id: "container1"
            });
        };
    </script> 

上面的初始化代码显示了最小化的初始化。还有更多参数可用于修改仪表盘功能和外观。

这张图片说明了 gaugeSVG 的各个部分,其中大部分是可配置的。

可能的初始化参数是:

  • title:[string] 显示在仪表盘上方的标题文本。它可以是空 string 以隐藏。默认是空 string
  • titleColor:[#rrggbb] 标题颜色。默认是 "#888888"。
  • value:[float] 要显示的值。默认是 (max - min) / 2.0。低于 min 的值显示为 min。高于 max 的值显示为 max
  • valueColor:[#rrggbb] 值文本颜色。默认是 "#000000"。准确的值以文本形式显示在仪表盘中心。
  • label:[string] 显示在值文本下方的标签。它可以是空字符串以隐藏。默认是空字符串。通常用于显示值的测量单位。
  • labelColor:[#rrggbb] 标签文本颜色。默认是 "#888888"。
  • min:[float] 仪表盘显示范围的最小值。如果 showMinMax 为 true,则将作为文本显示在仪表盘的起点。
  • max:[float] 仪表盘显示范围的最大值。如果 showMinMax 为 true,则将作为文本显示在仪表盘的起点。
  • showMinMax:[bool] 隐藏或显示 minmax 仪表盘显示范围的值作为文本。默认是 true
  • minmaxColor:[#rrggbb] minmax 值文本的颜色。默认是 "#888888"。
  • canvasBackColor:[#rrggbb] 仪表盘画布的背景颜色。默认是 "#f8f8f8"。
  • gaugeWidthScale:[float] 仪表盘弧的宽度。默认是 1.0。有意义的值范围是 0.15 到 1.5。较小的值显示较窄的弧,较大的值显示较厚的弧。
  • gaugeBorderColor: [float] 仪表盘弧的边框颜色。默认是 "#cccccc"。
  • gaugeBorderWidth: [#rrggbb] 仪表盘弧的边框宽度。默认是 0
  • gaugeBackColor:[#rrggbb] 仪表盘弧的背景颜色。默认是 "#cccccc"。
  • showGaugeShadow: [bool] 隐藏或显示仪表盘弧的阴影。默认是 true。仪表盘阴影由 SVG 径向渐变构成。渐变起始颜色是 gaugeShadowColor。渐变停止颜色是 gaugeBackColor
  • gaugeShadowColor: [#rrggbb] 仪表盘弧的阴影颜色。默认是 "#000000"。
  • gaugeShadowScale: [float] 仪表盘弧阴影的宽度。默认是 1.0。有意义的值范围是 0.8 到 1.5。较小的值显示较小的阴影,较大的值显示较厚的阴影。
  • lowerActionLimit:[float] 下操作限,如果不需要则为负值。默认是 (max - min) * 0.15 + min。
  • lowerWarningLimit:[float] 下警告限,如果不需要则为负值。默认是 (max - min) * 0.30 + min。
  • upperWarningLimit:[float] 上警告限,如果不需要则为负值。默认是 (max - min) * 0.70 + min。
  • upperActionLimit:[float] 上操作限,如果不需要则为负值。默认是 (max - min) * 0.85 + min。
  • needleColor:[#rrggbb] 仪表盘指针的颜色。默认是 "#444444"。
  • optimumRangeColor:[#rrggbb] 最佳范围颜色。默认是 "#44ff44"。
  • warningRangeColor:[#rrggbb] 警告范围颜色。默认是 "#ffff00"。
  • actionRangeColor:[#rrggbb] 操作范围颜色。默认是 "#ff4444"。

一个更强大的定制化初始化代码可能如下所示:

<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: 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> 

关注点

SVG 注入

gaugeSVG 开发的一个有趣方面是使用 JavaScript 动态地将 SVG 节点注入 DOM。实现这一目标的第一步是在 DOM 的 DIV 容器中创建 SVG 画布。

// 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);

下一步是将 SVG 节点插入 SVG 画布,例如背景矩形。

// 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); 

注意 createElementNSsetAttributeNSappendChild 的调用,并注意在正确的对象上调用这些方法以及将正确的参数作为参数传递。

SVG 文本

在 SVG 画布中绘制文本有点挑战性。这是因为 DOM 节点意外地嵌套在 SVG 节点中。

// 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); 

请注意,DOM 文本节点 (document.createTextNode(this.config.originalValue)) 必须创建为 SVG 文本节点的子节点 (this.gaugeVAL.appendChild(...);)。

更新

同样,指针位置和值文本可以在仪表盘生命周期/ HTML 文档显示期间更新,例如,以显示连续测量的最新值。这可以通过 gaugeSVGrefresh() 方法完成。实时更新非常容易实现,因为 DOM 节点和 SVG 节点的属性可以使用 JavaScript 进行操作。对于值文本,SVG 文本节点中唯一的 DOM 文本子节点必须设置为新的值文本。对于指针,'d' 属性必须使用新的路径进行更新。

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

这种方法不仅是最简单的方法,而且还能回收现有的 DOM 和 SVG 节点,避免了闪烁。

动画

指针位置的更新可以动画化,如果 gaugeSVG.refresh(valueNew, animated) 参数 animated 设置为 true。动画使用 JavaScript 的 setTimeout。为了实现视觉上吸引人的动画,值 this.animation.startIncrementDivisor 定义了从最后一个指针位置到新指针位置的差的 24 分之一作为初始增量步长。每个增量步长将以 this.animation.delay(15 毫秒)的延迟绘制。

为了模拟指针速度下降,增量步长乘以 0.966 的值 this.animation.decreaseOfIncrementValue 进行递减。为了防止无限动画,总增量步数限制为 this.animation.maxIncrements 的值 48。

这些 this.animation.* 值适用于更新间隔不小于 2 秒的情况。如果更新速度更快,则不应执行动画,或者必须调整这些值。任何调整都需要广泛的测试。

请注意,任何由 setTimeout() 调用的函数都将脱离 gaugeSVGthis 指针的上下文。为了在第一个定时函数调用时提供 gaugeSVG 的 this 指针,必须创建一个显式引用。所有后续调用都可以传递此引用。

// 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);

阴影 / 渐变

仪表盘背景可以绘制成具有阴影效果。这是通过径向渐变实现的。任何渐变都是定义 SVG 节点的一部分:document.createElementNS(svgns, 'defs') 所有定义 SVG 节点在整个文档中都是透明的。因此,如果在一个文档中显示多个仪表盘,则必须区分渐变定义: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);
}

径向渐变应绘制在应用于它的路径(仪表盘背景路径)的中心。最外层颜色环的中心相对于路径设置:setAttributeNS(null, 'cx', "50%")setAttributeNS(null, 'cy', "50%")。最内层颜色环也相对于路径设置:setAttributeNS(null, 'fx', "50%")setAttributeNS(null, 'fy', "50%")。因为仪表盘背景的路径具有 2:1 的宽高比,所以径向渐变看起来会是椭圆形的。为了实现完全圆形的渐变,应用了 1:2 的比例因子:setAttributeNS(null, 'gradientTransform', "scale(1 2)")。从最内层颜色到最外层颜色的渐变在渐变中心和渐变边框之间不均匀分布。渐变从中心到边框的 15% 开始:setAttributeNS(null, 'offset', "15%"),并在 setAttributeNS(null, 'offset', this.config.gaugeShadowScale * 33 + "%") 处结束,具体取决于 gaugeShadowScale

在定义了渐变之后,就可以应用它了。

// 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);
}

祝您使用 gaugeSVG 愉快!

历史

  • 文章的第一个版本来自 2013 年 6 月 18 日。
  • 2014 年 12 月 15 日的第二个版本更新了 JavaScript 源,以支持负显示范围,例如从 min: -80 到 max: -20,其中 lowerActionLimit: -50 和 upperActionLimit: -30。
  • 2015 年 10 月 6 日的第二个版本的补丁更新了 JavaScript 源,以修复显示范围从负值到正值以及禁用下警告限时出现的问题。
© . All rights reserved.