cytoscape.js - 图中的弹性(动画)边,节点对邻居施加引力?
问题描述
是否有 Cytoscape 的布局扩展可以实现类似于 Neo4j 浏览器的动态行为,即当您拖动节点时,它的边缘在一定程度上是弹性的,但也会沿部分方式拖动连接的节点(我会称之为局部重力)?
更新:我正在处理的示例使用CoSE Bilkent (compound) layout extension,但它似乎不支持我开箱即用的效果/动画。我曾希望几乎没有记录 { gravity: 1 }
的选项可能是相关的——毕竟,当一个节点拉动其他节点时,这很可能被描述为重力——但是在一个小样本图上更改设置没有这样的效果;随后进行了一些挖掘,我现在认为该设置与显示图形中心节点的距离有关。然后我查看了所有布局演示,似乎没有一个显示我想要的效果。AllegroViva 似乎实现了类似的行为(视频),但据我了解,它在该视频中呈现时间序列(因此可能只是手动重绘图表)。他们的网站似乎也失效了。
我正在寻找的是简单的东西,最好是开箱即用的东西,或者可以快速实施的东西。不幸的是,我不太确定我想要的正确搜索词是什么(尤其是在 Cytoscape 的领域)。因此,我问什么是公认的非常高级的问题。今天,我发现Visjs 将其称为物理事件(或者至少使用我所追求的效果作为该演示的一部分)——但正如我所提到的,gravity
Cytoscape 显然不是我想要的。然而,在这一点上,我只是想确定 Cytoscape 是否是正确的库,或者我是否需要查看其他库,例如Alchemy(参见其 Philosophers' Relatedness 示例)。
解决方案
回答:
感谢您编辑问题,我想我可以帮助您解决这个问题。Cytoscape.js 将此功能深埋在扩展布局中,特别是在cytoscape.js-cola中。布局是具有“弹性”节点的物理布局,因此它们之间的距离保持不变。这里的问题是,在Notes段落中,作者描述了这一点:
- 如果你想保持交互性,你可能不应该将infinite: true 与fit: true 混为一谈。拟合自然会改变缩放级别,使拖动错位并让用户感到奇怪——尽管它在技术上仍然有效。最好只适合:无限时为假:真,而 cy.center() 或 cy.fit() 在 layoutready 上。
- 对齐选项不如原始可乐选项灵活。这里只能使用整数来指定相对定位,所以有点局限。如果您想查看更复杂的实现,请发送拉取请求。
代码:
所以这个例子应该可以工作(请注意,节点可以移动,但由于它们已经处于最佳位置,它们往往会保持在编队状态):
document.addEventListener("DOMContentLoaded", function() {
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
autounselectify: true,
boxSelectionEnabled: false,
layout: {
name: "cola",
infinite: true,
fit: false
},
style: [{
selector: "node",
css: {
"background-color": "#f92411"
}
},
{
selector: "edge",
css: {
"line-color": "#f92411"
}
}
],
elements: {
nodes: [{
data: {
id: "1",
label: "P"
}
},
{
data: {
id: "2",
label: "sucrose phosphate phosphatase"
}
},
{
data: {
id: "4",
label: "sucrose 6-phosphate"
}
},
{
data: {
id: "6",
label: "sucrose"
}
},
{
data: {
id: "8",
label: "invertase"
}
},
{
data: {
id: "10",
label: "fructose"
}
},
{
data: {
id: "12",
label: "fructokinase"
}
},
{
data: {
id: "14",
label: "fructose 6-phosphate"
}
},
{
data: {
id: "20",
label: "phosphoglucose isomerase"
}
},
{
data: {
id: "22",
label: "glucose 6-phosphate"
}
},
{
data: {
id: "28",
label: "glucose"
}
},
{
data: {
id: "30",
label: "hexokinase"
}
},
{
data: {
id: "33",
label: "sucrose synthase"
}
},
{
data: {
id: "36",
label: "UDP - glucose"
}
},
{
data: {
id: "38",
label: "sucrose phosphate synthase"
}
},
{
data: {
id: "41",
label: "UDP"
}
},
{
data: {
id: "44",
label: "fructose 6-phosphate"
}
},
{
data: {
id: "46",
label: "ATP"
}
},
{
data: {
id: "47",
label: "ATP"
}
},
{
data: {
id: "52",
label: "ATP"
}
},
{
data: {
id: "57",
label: "ADP"
}
},
{
data: {
id: "66",
label: "PP"
}
},
{
data: {
id: "71",
label: "UTP"
}
},
{
data: {
id: "76",
label: "UDP glucose pyrophosphorylase"
}
},
{
data: {
id: "80",
label: "glucose 1-phosphate"
}
},
{
data: {
id: "86",
label: "phospho- glucomutase (cPGM)"
}
},
{
data: {
id: "89",
label: "G1P transporter"
}
},
{
data: {
id: "90",
label: "P"
}
},
{
data: {
id: "95",
label: "P"
}
},
{
data: {
id: "102",
label: "P"
}
},
{
data: {
id: "103",
label: "P"
}
},
{
data: {
id: "104",
label: "G6P transporter"
}
},
{
data: {
id: "109",
label: "glucose 6-phosphate"
}
},
{
data: {
id: "115",
label: "phospho- glucomutase (cPGM)"
}
},
{
data: {
id: "121",
label: "glucose 1-phosphate"
}
},
{
data: {
id: "128",
label: "ADPglucose pyrophosphorylase (pAGPase)"
}
},
{
data: {
id: "130",
label: "ADP - glucose"
}
},
{
data: {
id: "136",
label: "PP"
}
},
{
data: {
id: "141",
label: "ATP"
}
},
{
data: {
id: "148",
label: "inorganic diphosphatase"
}
},
{
data: {
id: "149",
label: "P"
}
},
{
data: {
id: "156",
label: "phosphate transporter"
}
},
{
data: {
id: "158",
label: "P"
}
},
{
data: {
id: "164",
label: "starch synthase (simpl.)"
}
},
{
data: {
id: "166",
label: "ADP"
}
},
{
data: {
id: "172",
label: "starch"
}
},
{
data: {
id: "178",
label: "ATP/ADP transporter"
}
},
{
data: {
id: "179",
label: "ADP"
}
},
{
data: {
id: "184",
label: "ADP"
}
},
{
data: {
id: "189",
label: "ATP"
}
}
],
edges: [{
data: {
source: "2",
target: "1"
}
},
{
data: {
source: "4",
target: "2"
}
},
{
data: {
source: "2",
target: "6"
}
},
{
data: {
source: "6",
target: "8"
}
},
{
data: {
source: "8",
target: "10"
}
},
{
data: {
source: "12",
target: "14"
}
},
{
data: {
source: "14",
target: "20"
}
},
{
data: {
source: "20",
target: "22"
}
},
{
data: {
source: "8",
target: "28"
}
},
{
data: {
source: "28",
target: "30"
}
},
{
data: {
source: "30",
target: "22"
}
},
{
data: {
source: "6",
target: "33"
}
},
{
data: {
source: "33",
target: "10"
}
},
{
data: {
source: "33",
target: "36"
}
},
{
data: {
source: "36",
target: "38"
}
},
{
data: {
source: "38",
target: "4"
}
},
{
data: {
source: "38",
target: "41"
}
},
{
data: {
source: "41",
target: "33"
}
},
{
data: {
source: "44",
target: "38"
}
},
{
data: {
source: "52",
target: "12"
}
},
{
data: {
source: "12",
target: "57"
}
},
{
data: {
source: "46",
target: "30"
}
},
{
data: {
source: "30",
target: "47"
}
},
{
data: {
source: "71",
target: "76"
}
},
{
data: {
source: "76",
target: "66"
}
},
{
data: {
source: "76",
target: "36"
}
},
{
data: {
source: "80",
target: "76"
}
},
{
data: {
source: "22",
target: "86"
}
},
{
data: {
source: "86",
target: "80"
}
},
{
data: {
source: "95",
target: "89"
}
},
{
data: {
source: "89",
target: "90"
}
},
{
data: {
source: "102",
target: "104"
}
},
{
data: {
source: "80",
target: "89"
}
},
{
data: {
source: "104",
target: "109"
}
},
{
data: {
source: "115",
target: "109"
}
},
{
data: {
source: "121",
target: "89"
}
},
{
data: {
source: "121",
target: "115"
}
},
{
data: {
source: "121",
target: "128"
}
},
{
data: {
source: "128",
target: "130"
}
},
{
data: {
source: "141",
target: "128"
}
},
{
data: {
source: "128",
target: "136"
}
},
{
data: {
source: "136",
target: "148"
}
},
{
data: {
source: "148",
target: "149"
}
},
{
data: {
source: "149",
target: "156"
}
},
{
data: {
source: "156",
target: "158"
}
},
{
data: {
source: "130",
target: "164"
}
},
{
data: {
source: "164",
target: "166"
}
},
{
data: {
source: "178",
target: "179"
}
},
{
data: {
source: "184",
target: "178"
}
},
{
data: {
source: "178",
target: "189"
}
},
{
data: {
source: "141",
target: "178"
}
},
{
data: {
source: "104",
target: "103"
}
},
{
data: {
source: "10",
target: "12"
}
},
{
data: {
source: "164",
target: "172"
}
},
{
data: {
source: "22",
target: "104"
}
}
]
}
}));
cy.unbind("tapend");
cy.bind("tapend", "node", function() {
cy.animate({
fit: {
eles: cy.elements(),
padding: 20
},
center: {
eles: cy.elements()
}
}, {
duration: 500
});
});
});
body {
font-family: helvetica;
font-size: 14px;
}
#cy {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 999;
}
h1 {
opacity: 0.5;
font-size: 1em;
}
<!DOCTYPE>
<html>
<head>
<title>cytoscape-cola.js demo</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<!-- for testing with local version of cytoscape.js -->
<!--<script src="../cytoscape.js/build/cytoscape.js"></script>-->
<script src="https://unpkg.com/webcola/WebCola/cola.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-cola@2.3.0/cytoscape-cola.min.js"></script>
</head>
<body>
<h1>cytoscape-cola demo</h1>
<div id="cy"></div>
</body>
</html>
结论:
Cytoscape.js 具有显示力布局的能力,但它比您提供的两个示例差一点(它们可以在不将节点移动到最佳位置但用户更喜欢的位置的情况下显示图形)。如果您喜欢使用 cytoscape.js,您可以这样做,但始终考虑您需要哪些用例并检查是否有任何其他应用程序可以更好地处理这种情况(cytoscape.js 有很多很棒的功能可供您使用,所以如果你能让它工作,细胞景观将是我的方式)。
祝你好运!
推荐阅读
- apache-spark - TIMESTAMP 在蜂巢中与镶木地板的行为不符
- hive - Hive SQL - 条件计数
- python - 如何使用来自 BERT 的嵌入来比较句子相似度
- kubernetes - Kubernetes Ingress 返回 502 Bad Gateway
- git - GitHub 如何处理移动的 Pull Request?
- php - 无法连接:用户“用户名”@“本地主机”的访问被拒绝(使用密码:是)
- python - Pandas - Groupby 或将多个数据帧剪切到垃圾箱
- python-3.x - Python 返回状态 200,但没有响应数据
- qt - Qt QQuickWidget 与 QGraphicsVideoItem 冲突
- javascript - Gatsby/React - 获取引荐网址