这是您应用的大部分内容:https://gojs.net/extras/splicing.html。完整的实现代码在页面上——只需执行“查看页面源代码”浏览器命令。但无论如何我都会在这里复制它:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Simple Splicing Sample for GoJS</title>
<meta name="description" content="Splicing a new node into a selected link, and splicing out an existing node in the path of a link chain." />
<!-- Copyright 1998-2018 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="https://unpkg.com/gojs"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script id="code">
function init() {
if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
var $ = go.GraphObject.make;
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
initialContentAlignment: go.Spot.Center,
// create a node upon double-click in background:
"clickCreatingTool.archetypeNodeData": { text: "a new node" },
"undoManager.isEnabled": true
});
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ locationSpot: go.Spot.Center },
new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
{
selectionAdornmentTemplate:
$(go.Adornment, "Spot",
$(go.Panel, "Auto",
$(go.Shape, { fill: null, stroke: "dodgerblue", strokeWidth: 2 }),
$(go.Placeholder, { margin: 1 })
),
$(go.Shape, "XLine",
{
isActionable: true, // so that click works in an Adornment
width: 14, height: 14, stroke: "orange", strokeWidth: 4, background: "transparent",
alignment: go.Spot.TopRight,
click: function(e, shape) {
var node = shape.part.adornedPart;
spliceNodeOutFromLinkChain(node);
},
cursor: "pointer"
},
new go.Binding("visible", "", function(ad) {
var node = ad.adornedPart;
return maySpliceOutNode(node);
}).ofObject())
)
},
$(go.Shape,
{
fill: "lightgray", strokeWidth: 0,
portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer"
},
new go.Binding("fill", "color")),
$(go.TextBlock, "some text",
{ margin: 8, editable: true },
new go.Binding("text").makeTwoWay())
);
myDiagram.linkTemplate =
$(go.Link,
{
relinkableFrom: true, relinkableTo: true,
reshapable: true, resegmentable: true,
selectionAdornmentTemplate:
$(go.Adornment,
$(go.Shape, { isPanelMain: true, stroke: "dodgerblue", strokeWidth: 2 }),
$(go.Shape, "PlusLine",
{
isActionable: true, // so that click works in an Adornment
width: 16, height: 16, stroke: "green", strokeWidth: 4, background: "transparent",
segmentOffset: new go.Point(8, 0),
click: function(e, shape) {
var link = shape.part.adornedPart;
var p0 = link.getPoint(0);
var p1 = link.getPoint(link.pointsCount - 1);
var pt = new go.Point((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
spliceNewNodeIntoLink(link, pt);
},
cursor: "pointer"
})
),
toShortLength: 1
},
new go.Binding("points").makeTwoWay(),
$(go.Shape, { strokeWidth: 2 }),
$(go.Shape, { toArrow: "OpenTriangle" })
);
function spliceNewNodeIntoLink(link, pt) {
link.diagram.commit(function(diag) {
var tokey = link.toNode.key;
// add a new node
var newnodedata = { text: "on link", location: go.Point.stringify(pt) };
diag.model.addNodeData(newnodedata);
// and splice it in by changing the existing link to refer to the new node
diag.model.setToKeyForLinkData(link.data, newnodedata.key);
// and by adding a new link from the new node to the original "toNode"
diag.model.addLinkData({ from: newnodedata.key, to: tokey });
// optional: select the new node
diag.select(diag.findNodeForData(newnodedata));
}, "spliced in node on a link");
}
function maySpliceOutNode(node) {
return node.findLinksInto().count === 1 &&
node.findLinksOutOf().count === 1 &&
node.findLinksInto().first() !== node.findLinksOutOf().first();
}
function spliceNodeOutFromLinkChain(node) {
if (maySpliceOutNode(node)) {
node.diagram.commit(function(diag) {
var inlink = node.findLinksInto().first();
var outlink = node.findLinksOutOf().first();
// reconnect the existing incoming link
inlink.toNode = outlink.toNode;
// remove the node and the outgoing link
diag.removeParts([node, outlink], false);
// optional: select the original link
diag.select(inlink);
}, "spliced out node from chain of links");
}
}
load();
}
// Show the diagram's model in JSON format that the user may edit
function save() {
document.getElementById("mySavedModel").value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
<div>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
Diagram Model saved in JSON format:
</div>
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "GraphLinksModel",
"nodeDataArray": [
{"key":1, "text":"Alpha", "color":"lightblue", "location":"-40 -10"},
{"key":2, "text":"Beta", "color":"orange", "location":"106 -58"},
{"key":3, "text":"Gamma", "color":"lightgreen", "location":"-8 105"},
{"key":4, "text":"Delta", "color":"pink", "location":"110 51"}
],
"linkDataArray": [
{"from":1, "to":2, "points":[-15.5,-18.0,84.5,-50.9]},
{"from":1, "to":3, "points":[-35.7,5.1,-12.2,89.85]},
{"from":2, "to":2, "points":[116.7,-42.8,150.3,4.3,59.3,0.3,93.8,-42.8]},
{"from":3, "to":4, "points":[23,90.8,87,61.5]},
{"from":4, "to":1, "points":[87,41.6,-15.5,0.0]}
]}
</textarea>
<p>
When a Link is selected it shows a green "+" button that when clicked creates a new Node and splices it into the link.
It does that by reconnecting the existing link to the new node and creating a new link to go from the new node to the original
destination of the selected link.
</p>
<p>
When a Node is selected, if it has exactly one Link coming into it and exactly one Link going out of it,
then it shows an orange "X" button. When clicked it removes the node and the outgoing link and reconnects the incoming
link to connect with the original destination of the outgoing link.
</p>
<p>
All links are relinkable and resegmentable.
</p>
</div>
</body>
</html>
在后台双击创建一个新节点。由以下人员实现:
"clickCreatingTool.archetypeNodeData": { text: "a new node" },
或 Control-拖动或 Control-C/Control-V 复制所选内容。
从节点边缘内部拖动以绘制到节点的新链接。由以下人员实现:
portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer"
在节点的Shape上。
单击选定节点的文本以开始编辑它。由以下人员实现:
editable: true
在Node中的TextBlock上。
每个链接在被选中时,允许用户拖动一端以重新连接它,或者通过拖动中间手柄来添加或删除点。由以下人员实现:
relinkableFrom: true, relinkableTo: true,
reshapable: true, resegmentable: true
在链接上。
每个链接还有一个自定义选择装饰,它显示一个绿色的“+”按钮,用于创建一个新节点并将其拼接到链接中:
selectionAdornmentTemplate:
$(go.Adornment,
$(go.Shape, { isPanelMain: true, stroke: "dodgerblue", strokeWidth: 2 }),
$(go.Shape, "PlusLine",
{
isActionable: true, // so that click works in an Adornment
width: 16, height: 16, stroke: "green", strokeWidth: 4, background: "transparent",
segmentOffset: new go.Point(8, 0),
click: function(e, shape) {
var link = shape.part.adornedPart;
var p0 = link.getPoint(0);
var p1 = link.getPoint(link.pointsCount - 1);
var pt = new go.Point((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
spliceNewNodeIntoLink(link, pt);
},
cursor: "pointer"
})
),
每个节点只有一个链接进入它,一个链接离开它(不是同一个链接)有一个选择装饰,包括一个橙色的“X”按钮,允许该节点被拼接到它们的路径之外.实现为:
selectionAdornmentTemplate:
$(go.Adornment, "Spot",
$(go.Panel, "Auto",
$(go.Shape, { fill: null, stroke: "dodgerblue", strokeWidth: 2 }),
$(go.Placeholder, { margin: 1 })
),
$(go.Shape, "XLine",
{
isActionable: true, // so that click works in an Adornment
width: 14, height: 14, stroke: "orange", strokeWidth: 4, background: "transparent",
alignment: go.Spot.TopRight,
click: function(e, shape) {
var node = shape.part.adornedPart;
spliceNodeOutFromLinkChain(node);
},
cursor: "pointer"
},
new go.Binding("visible", "", function(ad) {
var node = ad.adornedPart;
return maySpliceOutNode(node);
}).ofObject())
)
选择一个节点并用Delete键删除它不仅会删除该节点,还会删除与其相连的所有链接。
当然,完全支持撤消和重做。由以下人员实现:
"undoManager.isEnabled": true
一切都应该在触摸设备上运行,虽然我没有用这个示例尝试过。
而序列化和反序列化是通过在save和load函数中调用Model.toJson和Model.fromJson来完成的:
document.getElementById("mySavedModel").value = myDiagram.model.toJson();
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
当然,您需要向服务器发送和接收 JSON 格式的文本,而不是在