Is there a way in Vis.js to keep nodes at the same size while zooming in/out? - vis.js

I'm wondering whether there is or not a way to keep the size of nodes, in a Vis.js network, always at the same size while zooming in/out
I the Options
{
size: 10,
scaling: {
min: 10,
max: 10
}
}
but this setting doesn't work at all, I also tried with
customScalingFunction: function (min,max,total,value) {
return 1;
}
thinking that this function would run any time there is a re-rendering of the network.

This can be achieved by changing the nodes.size and nodes.font.size options when the network is zoomed. Details on these options can be found in the documentation here and details on the zoom event here. As per the documentation the nodes.size option is only applicable to shapes which do not have the label inside of them.
Example snippet adjusting the size on zoom:
// create an array with nodes
let nodes = new vis.DataSet([
{ id: 1, label: "Node 1" },
{ id: 2, label: "Node 2" },
{ id: 3, label: "Node 3" },
{ id: 4, label: "Node 4" },
{ id: 5, label: "Node 5" },
]);
// create an array with edges
let edges = new vis.DataSet([
{ from: 1, to: 3 },
{ from: 1, to: 2 },
{ from: 2, to: 4 },
{ from: 2, to: 5 },
{ from: 3, to: 3 },
]);
// create a network
const container = document.getElementById("mynetwork");
const data = {
nodes: nodes,
edges: edges,
};
let options = {
nodes: {
shape: 'dot',
size: 10,
font: {
size: 10
}
}
};
const network = new vis.Network(container, data, options);
// Set the initial node size once before network is drawn
network.once("beforeDrawing", function() {
setNodeSize(network.getScale());
});
// Adjust size on zoom
network.on("zoom", function (params) {
setNodeSize(params.scale);
});
function setNodeSize(scale){
// Update node size dependent on scale
options.nodes.size = (10 / scale);
options.nodes.font.size = (10 / scale);
network.setOptions(options);
}
#mynetwork {
width: 600px;
height: 160px;
border: 1px solid lightgray;
}
<script src="https://visjs.github.io/vis-network/standalone/umd/vis-network.min.js"></script>
<div id="mynetwork"></div>
Regarding the scaling options, I don't believe they can be used to achieve this. Scaling is relative to other nodes on the network, not the viewport / zoom. A quick example to illustrate changing the scaling value is https://jsfiddle.net/34b682qs/. Clicking on the button toggles the scaling value set for each node in sequence between 10 and 20. When all nodes have the same value the network scales them all to be the same size regardless of if that value is 10 or 20. Therefore adjusting this value on zoom would have no effect.

Related

How to visualize a vaadin web component

Follow the vaadin guidence of web component to create a meter:
#Tag("dw-meter")
#NpmPackage(value = "echarts", version = "5.2.2")
#JsModule("../node_modules/echarts/dist/echarts.js")
#JsModule("./dwmeter.webcomponent.js")
public class DwMeter extends Div {
}
Integrate the meter into a demo application:
DwMeter meter = new DwMeter();
meter.setWidth("100px");
meter.setHeight("100px");
add(meter);
Application is executed successfully, but the meter is not displayed.
Trace the web page, Tag <dw-meter> and <canvas> are generated correctly :
Changed Tag <dw-meter> to <div>, the meter is visible:
My question is how to visualize an user-defined vaadin web component? e.g. <dw-meter>
attached dwmeter.webcomponent.js:
import * as ECharts from "echarts";
class dwMeter extends HTMLElement {
constructor() {
super();
}
init(o) {
// Shadow root
const shadowRoot = this.attachShadow({mode: 'open'});
// container
var container = document.createElement('div');
shadowRoot.appendChild(container);
// Garantee all elements are rendered
setTimeout(function() {
var myChart = ECharts.init(o); //container
myChart.setOption(o.options());
}, 0);
}
options() {
const gaugeData = [
{
value: 0.25,
name: 'pressure',
title: {
offsetCenter: ['0%', '90%']
}
}
];
var option = {
series: [
{
type: 'gauge',
min: 0,
max: 0.25,
splitNumber: 5,
progress: {
show: false,
width: 5
},
axisLine: {
lineStyle: {
width: 5,
color: [[1, 'rgba(36,177, 76)']]
}
},
axisTick: {
show: false
},
splitLine: {
length: -10,
lineStyle: {
width: 5,
color: 'rgba(36,177, 76)'
}
},
axisLabel: {
distance: -20,
color: '#999',
fontSize: 10
},
anchor: {
show: true,
showAbove: true,
size: 12,
itemStyle: {
borderWidth:0,
color: 'rgba(36,177, 76)',
}
},
pointer: {
icon: 'path://M2.9,0.7L2.9,0.7c1.4,0,2.6,1.2,2.6,2.6v115c0,1.4-1.2,2.6-2.6,2.6l0,0c-1.4,0-2.6-1.2-2.6-2.6V3.3C0.3,1.9,1.4,0.7,2.9,0.7z',
width: 5,
length: '60%',
offsetCenter: [0, '8%'],
itemStyle: {
color: 'rgba(36,177, 76)'
}
},
title: {
color: 'rgba(36,177, 76)',
fontSize: 14,
fontWeight: 800,
fontFamily: 'Arial',
offsetCenter: [0, '100%']
},
detail: {
valueAnimation: true,
fontSize: 12,
offsetCenter: [0, '55%'],
show: true
},
data:gaugeData
}
]
};
return option;
}
connectedCallback() {
this.init(this);
}
disconnectedCallback() {
}
attributeChangedCallback() {
}
clone(origin, target) {
var target = target || {};
for(var prop in origin) {
target[prop] = origin[prop];
}
}
}
window.customElements.define('dw-meter', dwMeter);
The reason that nothing is shown when you're using the <dw-meter> custom element is that it has a shadow root, while the actual content (rendered by the ECharts library) is outside that shadow root. Whenever an element has a shadow root, then the content of the shadow root will be rendered and the content outside the shadow root will be rendered in the location of a <slot> element inside the shadow root. If there is no <slot>, then it won't be shown anywhere at all.
If you want to use the shadow root to encapsulate styles, then you would at the very least need to change ECharts.init(o) to instead do ECharts.init(container). There might also be other things that you need to change to make it work properly, but that depends on exactly how ECharts is implemented. The o parameter that I assume you're passing from the server is most likely redundant since this is already a reference to the top-level element.

Why is this 5 body network constantly rotating?

I could not understand why this simple network configuration keeps spinning around node 2, except after some nudges around 30s mark in this screen cast, after which it restarts spinning. The setup uses visjs network module with forceatlas2 resolver.
My options param for the Network constructor is as follows:
get options(): Options {
return (
this.optionS || {
nodes: {
shape: 'dot',
size: 30,
font: {
size: 32
},
borderWidth: 2,
shadow: true
},
edges: {
width: 2,
shadow: true,
smooth: {
enabled: true,
roundness: 0.5,
type: 'cubicBezier',
forceDirection: 'vertical'
}
},
physics: {
forceAtlas2Based: {
avoidOverlap: 0.25,
gravitationalConstant: -95,
centralGravity: 0.01,
springLength: 100,
springConstant: 0.19,
nodeDistance: 175,
damping: 0.11
},
minVelocity: 0.75,
solver: 'forceAtlas2Based'
}
}
);
}
The host angular component provides these 5 nodes:
const nodes = new DataSet([
{ id: 1, label: 'Node 1' },
{ id: 2, label: 'Node 2' },
{ id: 3, label: 'Node 3' },
{ id: 4, label: 'Node 4' },
{ id: 5, label: 'Node 5' }
]);
const edges = new DataSet([
{ from: 1, to: 3 },
{ from: 1, to: 2 },
{ from: 2, to: 4 },
{ from: 2, to: 5 }
]);
this.graphData = { nodes, edges };
The network directive simply instantiates the Network as follows:
this.network = new Network(
this.el.nativeElement,
this.graphData,
this.options
);
Any insight into why this sort of perennial motion happens would be appreciated. I need to understand what factors to keep in mind for generating 'stable' nodes so that users do not have to keep chasing nodes/edges to click/interact with.
Increase minVelocity or damping to stop this.
The way you configured it the physics actually never stops moving the nodes around. Nodes 1, 2, 4 and 5 are kept in star arrangement by central gravity. Node 3 then pushes node 1 away but since everything's connected it ends up moving all of the nodes. Thanks to the edge between node 1 and 3 the force is stronger in one direction than the other. This then applies never ending force on the whole arrangement which ends up slowly spinning around node 2.
Faster spinner:

Cytoscape height canvas fixed to 0

I'm using cytoscape.js and when I try to initialize it, I found the canvas height to 0. I don't understand why.
This is my js :
var cy = cytoscape({container: document.getElementById("mapping")});
cy.add([
{group: "nodes", data: {id: "n0"}, position: {x:0, y:0}},
{group: "nodes", data: {id: "n1"}, position: {x:100, y:50}},
{group: "edges", data: {id: "e0", source: "n0", target: "n1"}}
]);
console.log(cy.container());
Here is the jsfiddle where you can see the "height":0px in log and nothing in rendered.
If you initialize cytoscape with static data, consider doing it like the documentation shows:
var cy = cytoscape({
container: document.getElementById('cy'), // container to render in
elements: [ // list of graph elements to start with
{ // node a
data: { id: 'a' }
},
{ // node b
data: { id: 'b' }
},
{ // edge ab
data: { id: 'ab', source: 'a', target: 'b' }
}
],
style: [ // the stylesheet for the graph
{
selector: 'node',
style: {
'background-color': '#666',
'label': 'data(id)'
}
},
{
selector: 'edge',
style: {
'width': 3,
'line-color': '#ccc',
'target-arrow-color': '#ccc',
'target-arrow-shape': 'triangle'
}
}
],
layout: { /!!!
name: 'grid',
rows: 1
}
});
you dont specify a layout in your sample code, i rarely do it like that, i simply add the nodes/edges and call the layout algorithm i want, the rest of your code seems ok, did you include all scripts and css files for cytoscape?

Auto Adjust Size of Modal Content in Bootstrap Modal

i am new to ASP.net MVC and Bootstrap. I am using this graph from chart.js as reference. https://canvasjs.com/docs/charts/integration/asp-net-mvc-charts/
I was able to use the link to put the modal content inside a modal. However, there are unusual behavior that is happening upon modal pop up showing the graph. The graph does not auto-adjust in the modal size, UP UNTIL i minimize the browser and maximize.
upon first load, here's the image
after I minimize and maximize the browser, it is back to its usual form, as it auto adjusts in the modal size
here's the code
_partialView.cshtml
<div id="chartContainer" > </div>
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<script type="text/javascript">
//function doFunction() {
var chart = new CanvasJS.Chart("chartContainer", {
theme: "theme2",
animationEnabled: true,
title: {
text: "Simple Column Chart in ASP.NET MVC"
},
subtitles: [
{ text: "Try Resizing the Browser" }
],
data: [
{
type: "column", //change type to bar, line, area, pie, etc
dataPoints: [
{ x: 10, y: 71 },
{ x: 20, y: 55 },
{ x: 30, y: 50 },
{ x: 40, y: 65 },
{ x: 50, y: 95 },
{ x: 60, y: 68 },
{ x: 70, y: 28 },
{ x: 80, y: 34 },
{ x: 90, y: 14 }
]
}
]
});
chart.render();
//};
</script>
I found a workaround to this problem with Canvas Chart.js.
Based on this similar question, the author or developer has addressed the problem and give this solution https://canvasjs.com/forums/topic/charts-arent-full-size-until-page-is-refreshed/
This might help you, if you wish to use this plug-in in your future work.
Just put this line of codes that acts as basically creating the charts after a couple of seconds of delay after the page load event
var chart = null;
setTimeout(function(){
chart = [create chart here]
chart.render();
},3000);
So the modal will pop-up, and the chart will be displayed with a little delay based on the seconds you input

Kendo Diagram Shapes Centered

I'm following the example from the Telerik web site for Basic Usage with the exception of using a Model, children Heirarchy. I just want to be able to list shapes with text boxes and be able to connect them and get the connections later. So far, I'm able to list the shapes and the text boxes, but for some reason all the shapes get centered to the origin of the diagram. I'd like to be able to list the shapes in some order, without connections, then connect them later on on the diagram. Here is the code I have so far:
var data = [{
firstName: "Antonio",
lastName: "Moreno",
title: "Team Lead",
colorScheme: "#1696d3"
},
{
firstName: "Alfredo",
lastName: "Morales",
title: "Team Lead",
colorScheme: "#1696d3"
}];
function visualTemplate(options) {
var dataviz = kendo.dataviz;
var g = new dataviz.diagram.Group();
var dataItem = options.dataItem;
g.append(new dataviz.diagram.Rectangle({
width: 210,
height: 75,
stroke: {
width: 0
},
fill: {
gradient: {
type: "linear",
stops: [{
color: dataItem.colorScheme,
offset: 0,
opacity: 0.5
}, {
color: dataItem.colorScheme,
offset: 1,
opacity: 1
}]
}
}
}));
g.append(new dataviz.diagram.TextBlock({
text: dataItem.firstName + " " + dataItem.lastName,
x: 85,
y: 20,
fill: "#fff"
}));
g.append(new dataviz.diagram.TextBlock({
text: dataItem.title,
x: 85,
y: 40,
fill: "#fff"
}));
return g;
}
function createDiagram() {
$("#diagram").kendoDiagram({
dataSource: new kendo.data.HierarchicalDataSource({
data: data,
}),
shapeDefaults: {
visual: visualTemplate
},
});
var diagram = $("#diagram").getKendoDiagram();
diagram.bringIntoView(diagram.shapes);
}
$(document).ready(createDiagram);
I've made some sample: http://dojo.telerik.com/UbECE that makes rectangles one next to another.
I am following this example from API Documentation http://docs.telerik.com/kendo-ui/api/javascript/dataviz/ui/diagram#configuration-shapeDefaults.visual
They are using:
$("#diagram").getKendoDiagram().layout();
which works.
Your function will became:
function createDiagram() {
$("#diagram").kendoDiagram({
dataSource : data,
shapeDefaults: {
visual: visualTemplate
},
});
$("#diagram").getKendoDiagram().layout();
}

Resources