Cytoscape add custom image to node with clickable items on it
Asked Answered
S

1

8

I am building a charting tool in ember using cytoscape js and I can render chart data however I do not know how to set each node to display with a image that has other images/buttons that function within it. Basically I want it to look like this:

enter image description here

In the image there are two buttons (I will most likely add icons as well) and also there are labels that exist within the node which I don't know how to do either.

Here is the code I currently have.

Template:

<div class="container" >
  <div id="cy"></div>
</div>

Component JS:

import Ember from 'ember';


export default Ember.Component.extend({
tagName: '',

map: Ember.computed('model.map_data', function()
{
 if(this.get('model.map_data')){
   return JSON.parse(this.get('model.map_data').data)
  } else {
   return {};
  }
 }),
cytoscape_data: Ember.computed('model.sub_apps.[]',function() {
var ret = {
        nodes: [],
        edges: []
};
var red = 50;//replace with threshold
var green = 25;//replace with threshold
var _this = this;
this.get("model").map_data.forEach(function(node) {
  var y= 0;
  var x = 0;
  var color = 'green';
  if(node.value >= red ){
    color = 'red';
  }else {
    if(node.value > green){
      color = 'orange';
    }
  }
  var position = _this.get("map")["app" + node.id];
  if(position){
    x = parseInt(position.split(',')[0]);
    y = parseInt(position.split(',')[1]);
  }
  ret["nodes"].push({
          data: {
                  id: node.id,
                  label: node.name,
                  node_type: 'app',
                  tooltip: node.description,
                  color: color
          },
          position: {
                  x: x,
                  y: y
          }
  });
  if(node.relations) {
    node.relations.forEach(function(parent) {

      ret["edges"].push({
        data: {
          source: node.id,
          target: parent.app_to_id
        }
      });
    });
  }
});

 return ret;
}),

didInsertElement: function() {
 this._super();
 var cy = cytoscape({
 container: Ember.$('#cy')[0],
 elements: this.get("cytoscape_data"),
 zoom: 1,
 pan: { x: 0, y: 0 },
 fit: true,
 randomize: false,
 layout: {
      name: 'preset'
    },
  style: [
    {
      selector: 'node',
      style: {
        'content': 'data(label)',
        'text-opacity': 0.8,
        'text-valign': 'center',
        'text-halign': 'right',
        'width': '200px',
        'height': '200px',
        'border-color': 'green',
        'border-width': 3,
        'border-opacity': 0.5,
        'background-image': 'url(../assets/images/base_node_image.svg)'
        // 'background-color': 'data(color)'
      }
    },
    {
      selector: 'edge',
      style: {
        'width': 6,
        'border-color': 'green',
        'target-arrow-shape': 'triangle',
        'target-arrow-color': 'red',
        'opacity': 1,
        'curve-style': 'bezier'
      }
    },

    {
      selector: ':selected',
      style: {
        'background-color': 'orange',
        'opacity': 1
      }
    },

    {
      selector: '.faded',
      style: {
        'opacity': 0.0,
        'text-opacity': 0
      }
    },
  ],

});
Ember.run.scheduleOnce('afterRender', this, function(){
  cy;
});
cy.on('click', 'node', function(evt){
  var node = evt.target;
  console.log( 'clicked ' + node.data('label') );
   });
  },
});

The chart this code renders looks like this:

enter image description here

I can display a background-image however it displays in a circle which I dont know how to get rid of. The color of the circle is determined by some logic above which was a test to see if it works and that is fine (going to use that for one of the icons on the node later). I can also display the label for the node but I don't know how to display that within the node itself.

Any help is appreciated, thanks!

Schroer answered 7/12, 2017 at 19:37 Comment(2)
If there is also an ability to represent nodes as ember components that would also accomplish all of these details and fulfill the bounty.Schroer
did yo ever have chance to look at the source code I have provided?Naphthalene
N
13

It is not so trivial to achieve what you want if not impossible. You say "set each node to display with a image that has other images/buttons that function within it."; this means you need to render html into a canvas; because what cytoscape puts as drawing area is an HTML canvas.

See @maxfranz's (author of cytoscape.js) for a relevant question; where he basically says "It's not possible to render HTML in a canvas, nor would you probably want to for performance".

This means putting html buttons, URLs might not be what you desire. See also MDN Web Docs for further explanation.

That said; I think you can still manage to achieve what you want; but with a different approach. You can make use of cytoscape's compound nodes. You can define the images and buttons as simple nodes and define compound nodes as surrounding containers. I have created a working example for you at the follwoing github repository.

The final result I got is as follows:

enter image description here

I hope this helps.

Naphthalene answered 12/12, 2017 at 15:34 Comment(5)
Thank you for this, I will be looking into this and hope if I have any questions or concerns I can post them here and hopefully you can help me with them. I've awarded you the bounty.Schroer
Thanks for the reward. I will do what I can for further questions. good luck.Naphthalene
One thing I am not sure about how to do, is I want to be able to click a node or a edge line (that connects the main node to children nodes) and have a pop up appear with buttons. One of those buttons should enable me to type a label for that edge line. Is this possible? This pop up would basically have two functions, one to remove the edge line and the other to add a label for the edge line. Appreciate any help.Schroer
i will update the project at github and let you know about it.Naphthalene
This was a helpful tip. It's 2020 and SVG scaling works better now cross-browser. I had to mess around with width/height, and viewBox in the svg, and Cytoscape style settings for the selectors, but got something working good enough, for dynamic node content, that scales well with Zooming.Camenae

© 2022 - 2024 — McMap. All rights reserved.