Creating SVG Diagrams

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.
Into the options object you can pass:

  • height: the height in pixel the SVG diagram will occupy vertically in the page.

  • viewportWidth: the width in pixels of the SVG viewport (default 1280).

  • viewportHeight: the height in pixels of the SVG viewport (default 720).


The SVG element will horizontally fill the parent widget.

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.
See SvgDiagram.makeDraggable() method.

foreignObject(id, x, y, w, h, html)

Embeds HTML element into an SVG element as foreign object.
Parameters:

  • id: the id of the foreign object being created.

  • x,y,w,h: the coordinates and size of the foreign object.

  • html: the string representing the HMTL element to append. When the HTML element is attached, you can manipulate it by using standard HTML functions or JQuery functions.

getById(id)

Retrieves an SVG object by id.
If missing null is returned.

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
AbstractWidget.loadResources(resources)

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.