我问了一个关于StackOverflow(Seeking Javascript library for displaying and editing networks of nodes and edges)的问题,并指出了gojs拼接示例。
这让我走了很长一段路,所以谢谢您的回答,但是我遇到了试图获得我想要的行为的砖墙。
我要创建的应用程序是在地图上编辑边框:
因此,节点和链接未标记(节点只是小圆圈,链接只是折线)。
我已尝试适当修改接头样本(https://gojs.net/temp/splicing.html)。除示例功能之外,我还需要以下关键功能:
(现有示例将新节点与现有节点等距放置,并使用直接链接。)
我试图创建的用户体验是这样的:首先,选择链接,以得到其通常的装饰;然后在多段线上的一点上按住Shift键并单击装饰物,该点将成为新节点。
我试图通过使用https://gojs.net/latest/intro/extensions.html中描述的扩展机制(而不是创建子类)覆盖LinkReshapingTool的方法来做到这一点。
不过,无论我尝试了什么,我都无法留下折线。在我的代码运行之后,通过在Chrome DevTools调试器中检查图表数据模型,它显示正确(即,我可以在链接中看到正确的点数组)。但是,当我随后允许执行继续时,链接将不会按预期显示(它们是笔直的),并且,如果我随后查看数据模型,则多个点消失了,每个链接都有一个起点和终点。
例如,我尝试了各种尝试,但均未成功:
我当前的代码如下。请原谅这些粗俗的风格-我不是经验丰富的JavaScript程序员。
<!DOCTYPE html> <!-- HTML5 document type -->
<!--
Adapted from splicing example from gojs.net
-->
<html>
<head>
<!-- use go-debug.js when developing and go.js when deploying -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gojs/1.8.28/go-debug.js"></script>
</head>
<body>
<div id="myDiagramDiv"
style="width:400px; height:300px; background-color: #DAE4E4;"></div>
<script>
var $ = go.GraphObject.make;
// state variables to remember what to do after reshaping tool aborted
var doSpliceNode = false;
var doSpliceIntoLink = null;
var doSplicePoint = null;
var doSpliceIndex = -1;
// diagram
var myDiagram = $(go.Diagram, "myDiagramDiv",
{
"undoManager.isEnabled": true
});
var tool = myDiagram.toolManager.linkReshapingTool;
// Override doMouseDown on linkreshapingtool. If user clicks on a handle with SHIFT pressed, want to insert
// a new node at that point rather than use the default behaviour to (further) reshape the link, and moreover
// want to retain the points in the link. I.e. turn one of the points in the link into a new node.
// (Existing gojs splicing example does not do this: it just puts a new node at the midpoint of the existing
// link, with no regard to the points along the link.)
tool.doMouseDown = function() {
console.log("mousedown at (" + this.Fp.M + "," + this.Fp.N + ")");
console.log(" on link from " + this.adornedLink.fromNode + " to " + this.adornedLink.toNode);
console.log(" with shift pressed? " + myDiagram.lastInput.shift);
var spliced = false;
if (myDiagram.lastInput.shift)
{
// work out which of the points on the link was clicked
var link = this.adornedLink;
var numpts = link.pointsCount;
var i;
var x = this.Fp.M; // @@TODO - by inspection in debugger this contains the X coord, but what's documented place to get this?
var y = this.Fp.N; // @@TODO - ditto for Y coord
for (i = 1; !spliced && (i < numpts - 1); i++)
{
if ((link.getPoint(i).x == x) && (link.getPoint(i).y == y))
{
console.log(" .. at point " + i);
// Store off what to do. (This used to be done inline - deferred to after as one of the things
// to try to make it work.)
doSpliceNode = true;
doSpliceIntoLink = link;
doSplicePoint = new go.Point(x, y);
doSpliceIndex = i;
spliced = true;
}
}
}
//if (!doSpliceNode)
{
console.log(".. call base class doMouseDown");
go.LinkReshapingTool.prototype.doMouseDown.call(tool);
}
}
// Override doMouseUp as well. If we had decided during mousedown to do the splice, then stop the tool now.
tool.doMouseUp = function()
{
// First call base class
go.LinkReshapingTool.prototype.doMouseUp.call(tool);
if (doSpliceNode)
{
// Doing splice - stop tool
console.log("STOP TOOL");
this.stopTool();
this.doDeactivate();
}
}
// Finally, override doStop to actually do the splice
tool.doStop = function() {
console.log("doStop");
// First call base class
go.LinkReshapingTool.prototype.doStop.call(tool);
if (doSpliceNode)
{
// now splice the node
console.log("splice node");
spliceNewNodeIntoLink2(doSpliceIntoLink, doSplicePoint, doSpliceIndex); // @@TODO make it respect points in existing link before and after
}
// Reset everything
doSpliceNode = false;
doSpliceIntoLink = null;
doSplicePoint = null;
doSpliceIndex = -1;
}
// Debug variable for inspecting later - not functional
var debugLastLink = null;
// Model, node and links for this application. Based heavily on https://gojs.net/temp/splicing.html and adapted as needed.
var myModel = $(go.GraphLinksModel);
myDiagram.nodeTemplate = $(go.Node,
"Auto",
new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
$(go.Shape, "Circle", { width: 6, height: 6, strokeWidth: 0 }));
myDiagram.linkTemplate =
$(go.Link,
{
relinkableFrom: true, relinkableTo: true,
reshapable: true, resegmentable: true,
/* selectionAdornmentTemplate: @@ COMMENT OUT - NOT NEEDED
$(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) {
alert(e);
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);
// @@TODO - instead, find the position where the mouse was clicked and place the node there
// ... need to work out which segment of polyline this was in so as to calculate new lines
// @@TODO - handle drag of node so that it just affects segments of lines immediately into it, rather than
// blatting over top of them
// @@TODO - what is object e and its components
spliceNewNodeIntoLink(link, pt);
},
cursor: "pointer"
})
), */
toShortLength: 1
},
new go.Binding("points").makeTwoWay(), // Use the points information from the linkDataArray initializer
$(go.Shape, { strokeWidth: 2 })
);
/* function spliceNewNodeIntoLink(link, pt) { // @@ original version no longer called
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");
} */
// Utility function used in one attempt to get this to work. Initializers in nodeDataArray do it via an array of numbers,
// so try that here.
function toArray(nodelist)
{
var returnarray = new Array();
var i;
for (i = 0; i < nodelist.size; i++)
{
var pt = nodelist.elt(i);
returnarray.push(pt.x);
returnarray.push(pt.y);
}
return returnarray;
}
// Function to splice the new node into the link. Parameters are
// - link: the link to splice into
// - pt: the point within the link to turn into a node
// - index: index into existing polyline of that point
function spliceNewNodeIntoLink2(link, pt, index) {
link.diagram.commit(function(diag) {
var oldlinkpointslist = link.points;
var link1pointslist = new go.List(go.Point);
var link2pointslist = new go.List(go.Point);
var i;
// Create new points list, from "from" node to new node to be added
for (i = 0; i <= index; i++)
{
var point = new go.Point(link.getPoint(i).x, link.getPoint(i).y);
link1pointslist.add(point);
}
console.log(link1pointslist);
// .. and from new node to "to" node
for (i = index; i < link.pointsCount; i++)
{
var point = new go.Point(link.getPoint(i).x, link.getPoint(i).y);
link2pointslist.add(point);
}
console.log(link2pointslist);
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);
// ** NEW CODE
// Code this was based on re-used the existing link, re-purposing it to go from "from" node
// to new node, so do the same, but give it a new points list.
link.points = link1pointslist; // @@TODO find out why this doesn't work
// ... actually it does, but something ditches the points later ...
// so maybe I need to move this code to after the tool has really finished operating
// by saving off the info and calling it in an override of the last tool method that
// gets called (perhaps not - did this and it didn't work)
debugLastLink = link; // @@TEMP
// and by adding a new link from the new node to the original "toNode"
// ** UPDATED to include the second new point list
diag.model.addLinkData({ from: newnodedata.key, to: tokey, points: toArray(link2pointslist) });
// optional: select the new node
diag.select(diag.findNodeForData(newnodedata));
}, "spliced in node on a link");
}
// not called at present
function maySpliceOutNode(node) {
return node.findLinksInto().count === 1 &&
node.findLinksOutOf().count === 1 &&
node.findLinksInto().first() !== node.findLinksOutOf().first();
}
// not called at present
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");
}
}
// Initialize modeldi
myModel.nodeDataArray = [
{ key: "1" , "location": "30 30" },
{ key: "2" , "location": "130 30" },
{ key: "3" , "location": "30 130" }
];
myModel.linkDataArray = [
{ from: "1", to: "2", "points": [ 30,30, 70,20, 100,40, 130,30 ] },
{ from: "2", to: "3", "points": [ 130,30, 100,80, 70,90, 30,130 ] },
{ from: "3", to: "1", "points": [ 30,130, 20,100, 40,70, 30,30 ] }
];
myDiagram.model = myModel;
</script>
</body>
</html>
答案 0 :(得分:0)
一些建议:
您遇到的问题是,当您创建一个新节点时,需要花费时间进行测量,安排和定位。这些活动中的任何一项都会使所有连接的链接的路由无效。显然,将链接与节点连接将使该链接的路由无效。因此,您必须确保一切按正确的顺序完成。