0

I'm trying to add badges to my cytoscape.js nodes. Badges are HTML elements. I'm using bootstrap badges

Here are elements with badges. (the colors of the badges are irrelevant) enter image description here

When I zoom out, the position of the badges is not set correctly. They go down and right a bit. WHY IS THAT?

badges goes down a bit

Here is my code to set the positions. I but badges to to top left of the node. But I remove width of the HTML element to make it look like inside the node

let z1 = cy.zoom() / 2; // badges look too big with normal size so I downscale them
// e is cytoscape.js element, a node
const p = e.renderedPosition();
const eW = e.renderedWidth() / 2;
const eH = e.renderedHeight() / 2;
// div is an HTML element which is the badge
const w = div.clientWidth;
div.style.transform = `translate(${p.x + eW - w * z1}px, ${p.y - eH}px) scale(${z1})`;
canbax
  • 3,432
  • 1
  • 27
  • 44

2 Answers2

1

I would personally prefer a solution using cytoscape.js resources/extensions, namely the popper.js extension.

As far as I understand your problem, you add bootstrap elements to cytoscape.js in some way (you didn't specify this, so I have to guess).

Nomrally, a sticky popper div does the trick for this problem:

var cy = (window.cy = cytoscape({
  container: document.getElementById("cy"),

  style: [{
      selector: "node",
      css: {
        content: "data(id)",
        "text-valign": "center",
        "text-halign": "center",
        height: "60px",
        width: "160px",
        shape: "round-rectangle"
      }
    },
    {
      selector: "edge",
      css: {
        "target-arrow-shape": "triangle"
      }
    }
  ],

  elements: {
    nodes: [{
        data: {
          id: "n0"
        }
      },
      {
        data: {
          id: "n1"
        }
      },
      {
        data: {
          id: "n2"
        }
      },
      {
        data: {
          id: "n3"
        }
      },
      {
        data: {
          id: "n4"
        }
      },
      {
        data: {
          id: "n5"
        }
      },
      {
        data: {
          id: "n6"
        }
      },
      {
        data: {
          id: "n7"
        }
      },
      {
        data: {
          id: "n8"
        }
      },
      {
        data: {
          id: "n9"
        }
      },
      {
        data: {
          id: "n10"
        }
      },
      {
        data: {
          id: "n11"
        }
      },
      {
        data: {
          id: "n12"
        }
      },
      {
        data: {
          id: "n13"
        }
      },
      {
        data: {
          id: "n14"
        }
      },
      {
        data: {
          id: "n15"
        }
      },
      {
        data: {
          id: "n16"
        }
      }
    ],
    edges: [{
        data: {
          source: "n0",
          target: "n1"
        }
      },
      {
        data: {
          source: "n1",
          target: "n2"
        }
      },
      {
        data: {
          source: "n1",
          target: "n3"
        }
      },
      {
        data: {
          source: "n4",
          target: "n5"
        }
      },
      {
        data: {
          source: "n4",
          target: "n6"
        }
      },
      {
        data: {
          source: "n6",
          target: "n7"
        }
      },
      {
        data: {
          source: "n6",
          target: "n8"
        }
      },
      {
        data: {
          source: "n8",
          target: "n9"
        }
      },
      {
        data: {
          source: "n8",
          target: "n10"
        }
      },
      {
        data: {
          source: "n11",
          target: "n12"
        }
      },
      {
        data: {
          source: "n12",
          target: "n13"
        }
      },
      {
        data: {
          source: "n13",
          target: "n14"
        }
      },
      {
        data: {
          source: "n13",
          target: "n15"
        }
      }
    ]
  },

  layout: {
    name: "dagre",
    padding: 5,
    rankSep: 100
  }
}));

var makeTippy = function(node, text) {
  var ref = node.popperRef();
  var dummyDomEle = document.createElement("div");

  var tip = tippy(dummyDomEle, {
    onCreate: function(instance) {
      instance.popperInstance.reference = ref;
    },
    lazy: false, // mandatory
    trigger: "manual", // mandatory

    // dom element inside the tippy:
    content: function() {
      var div = document.createElement("div");
      div.innerHTML = text;
      return div;
    },

    // your own preferences:
    arrow: false,
    placement: 'top-end',
    hideOnClick: false,
    multiple: true,
    sticky: true
  });

  return tip;
};

cy.ready(function() {
  cy.zoom(0.75);
  cy.center();
  cy.ready(function() {
    let nodes = cy.nodes();
    nodes.each(function(node) {
      let tippy = makeTippy(node, node.id());
      tippy.show();
    });
  });
});
body {
  font: 14px helvetica neue, helvetica, arial, sans-serif;
}

#cy {
  height: 100%;
  width: 100%;
  position: absolute;
  left: 0;
  top: 0;
}

.tippy-popper {
  transition: none !important;
}
<html>

<head>
  <script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
  <script src="https://unpkg.com/dagre@0.7.4/dist/dagre.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/cytoscape-dagre@2.1.0/cytoscape-dagre.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/cytoscape-popper@1.0.6/cytoscape-popper.min.js"></script>
  <script src="https://unpkg.com/tippy.js@5.1.3/dist/tippy-bundle.iife.min.js"></script>
  <link rel="stylesheet" href="https://unpkg.com/tippy.js@5.1.3/dist/tippy.css" />
</head>

<body>
  <div id="cy"></div>
</body>

</html>

This snippet cuts some of the container off, so try this in your application for better results and take a look at the extensions used here

Stephan T.
  • 5,843
  • 3
  • 20
  • 42
  • I tried to use that extension. But I found positioning with that extension more challenging because that extension uses another extension called https://popper.js.org/ and it does not give an option to put the labels at the center. Also, the extension uses an old version of popper.js – canbax Feb 05 '20 at 10:57
  • You can customize every popper element with css, so positioning is not an issue. – Stephan T. Feb 05 '20 at 13:39
0

The problem stems from CSS scale. When I scale the element, the center point of the element remains invariant. The below sample shows What I mean

div {
  position: absolute;
}
span {
  font-size: 64px;
}

.s1 {
  transform: scale(1);
  background: red;
}

.s2 {
  transform: scale(0.5);
  background: blue;
}
<div class="s1"><span>00</span></div>
<div class="s2"><span>00</span></div>

So I have to consider the center point of the div. Below code does that

let z1 = this._g.cy.zoom() / 2;
const bb = e.renderedBoundingBox({ includeLabels: false, includeOverlays: false });
const w = div.clientWidth;
const h = div.clientHeight;
const deltaW4Scale = (1 - z1) * w / 2;
const deltaH4Scale = (1 - z1) * h / 2;
div.style.transform = `translate(${bb.x2 - deltaW4Scale - w * z1}px, ${bb.y1 - deltaH4Scale}px) scale(${z1})`;
canbax
  • 3,432
  • 1
  • 27
  • 44