Scalable Vector Graphics (SVG) is a powerful and flexible format for displaying complex and dynamic diagrams on the web.
Unlike images, SVGs are resolution-independent and can be manipulated through CSS, JavaScript, and even create interactive and adaptive visualizations.
This article explores how SVG can be used to build complex diagrams that update dynamically based on metric data, also real-time.
Adding SVG into widgets
When defining a widget you can leverage a built-in JavaScript SvgDiagram
class based on the public SVG.js (v3.2.4) library, which provides a rich set of functions for creating and manipulating SVG elements.
To start using this class, you must include it into your widgetās Dependencies.
In the following widget code example, you can see how to use the SvgDiagram
class to create and update an SVG diagram.
class MySchemaWidget extends AbstractWidget {
constructor(config) {
config = config || {};
config.height = config.height || "480px";
config.width = config.width || "640px";
super(config);
}
onInit(context) {
let widget = this;
this.svgDiagram = new SvgDiagram();
let promises = [];
promises.push(super.onInit(context));
// load SVG.js resources
promises.push(this.loadResources(this.svgDiagram.getSvgResources()));
promises.push(this.loadMetricDefinitions(context));
return Promise.all(promises).then(function() {
widget.createOrUpdateSchema(context);
widget.setLoaderVisible(false);
}).catch(error => {
console.error(error);
widget.showError({ "message": "Error initializing SVG diagram" });
widget.setLoaderVisible(false);
});
}
createOrUpdateSchema(context) {
let widget = this;
let metricValue = parseInt(context.data[context.metrics[0].name].value) || 0;
let radius = metricValue * 10;
console.log(JSON.stringify(context.data));
console.log(context.metrics[0].name + ": " + radius);
if(!this.svgDiagram.draw){
// create the SVG root element
let draw = this.svgDiagram.createSvg(context.htmlElement, { 'height': parseInt(widget.config.height), 'viewportWidth': parseInt(widget.config.width), 'viewportHeight': parseInt(widget.config.height) });
// add the SVG elements
draw.line(0, 240, 640, 240).stroke({ width: 5, color: "#28a745"});
this.circle = draw.circle(radius).cx(320).cy(240).stroke({ width: 2, color: "#28a745"}).fill("#ffc107");
this.text = draw.text(metricValue.toString()).font({size: 24, color: "#28a745"}).cx(320).cy(240);
} else {
// update SVG elements
this.circle.radius(radius);
this.text.text(metricValue.toString()).cx(320).cy(240);
}
}
onDataUpdated(context) {
this.createOrUpdateSchema(context);
}
onInputUpdated(context) {
// handle inputs and update schema
}
onDestroy(context) {
super.onDestroy(context);
}
}
Running the code above will result in an SVG as follows.
If your widget needs to be updated frequently on the page, we recommend that you cache the SVG elements instead of recreating them each time an update occurs.
For instance, the createOrUpdateSchema()
method creates a new SVG element the first time, but the next times it is invoked (e.g. a metric is updated) the elements are already available and can therefore be directly updated. If the widget is destroyed, e.g. by navigating to another page, the associated SVG elements are destroyed along with it.
SvgDiagram Class Reference
The SvgDiagram
class has the following methods.
createSvg(htmlElement, options) | Creates a new SVG element attached to the given htmlElement.
This method returns the SVG Draw object instance, you can use to create SVG elements. |
enableDrag(element, enabled) | Enables or disable dragging on an SVG element. |
foreignObject(id, x, y, w, h, html) | Embeds HTML element into an SVG element as foreign object.
|
getById(id) | Retrieves an SVG object by id. |
getSvgResources() | Returns the list of additional resources (SVG.js) to be loaded by the page when the widget is displayed the very first time. You can use this array of URLs as input to the following method |
makeDraggable(element, onDragged) | Add draggable feature on an SVG element. When dragging terminates, the onDragged callback is invoked passing the new X and Y coordinates. |