在每个节点上应用 onmouseover - GOjs 库 - 泳道
Posted
技术标签:
【中文标题】在每个节点上应用 onmouseover - GOjs 库 - 泳道【英文标题】:Apply onmouseover on each node - GOjs library - swimlane 【发布时间】:2014-05-05 19:43:56 【问题描述】:我已经完成了first part,在每个节点上添加了颜色。下一步是如何在每个节点上添加 onmouseover,然后在属于悬停节点的子图(包括连接它的链接/线)上突出显示/应用样式。
有人知道吗?
这是我目前的代码:
<?php
$prospectus = array(
'subjects' => array(
'First Year' => array(
array('course_no' => 'CS 110' , 'color' => '#21ff1c'),
array('course_no' => 'CS 111' , 'color' => 'white'),
array('course_no' => 'MATH 1' , 'color' => 'white'),
array('course_no' => 'MATH 2' , 'color' => 'white'),
array('course_no' => 'ENGL 1' , 'color' => 'white'),
array('course_no' => 'REED 1' , 'color' => 'white'),
array('course_no' => 'PE 1' , 'color' => 'white'),
array('course_no' => 'CWTS/ROTC 11' , 'color' => 'white'),
array('course_no' => 'GUIDANCE 1' , 'color' => 'white'),
array('course_no' => 'CS 120' , 'color' => 'white'),
array('course_no' => 'CS 121' , 'color' => 'white'),
array('course_no' => 'CS 122' , 'color' => 'white'),
array('course_no' => 'MATH 4' , 'color' => 'white'),
array('course_no' => 'ENGL 2' , 'color' => 'white'),
array('course_no' => 'REED 2' , 'color' => 'white'),
array('course_no' => 'PE 2' , 'color' => 'white'),
array('course_no' => 'CWTS/ROTC 12' , 'color' => 'white'),
array('course_no' => 'GUIDANCE 2' , 'color' => 'white')
),
'Second Year' => array(
array('course_no' => 'CS 211' , 'color' => 'white'),
array('course_no' => 'CS 212' , 'color' => 'white'),
array('course_no' => 'CS 213' , 'color' => 'white'),
array('course_no' => 'ENGL 3' , 'color' => 'white'),
array('course_no' => 'MATH 6' , 'color' => 'white'),
array('course_no' => 'REED 3' , 'color' => 'white'),
array('course_no' => 'PE 3' , 'color' => 'white'),
array('course_no' => 'CS 221' , 'color' => 'white'),
array('course_no' => 'CS 222' , 'color' => 'white'),
array('course_no' => 'CS 223' , 'color' => 'white'),
array('course_no' => 'CS 224' , 'color' => 'white'),
array('course_no' => 'ENGL 4' , 'color' => 'white'),
array('course_no' => 'REED 4' , 'color' => 'white'),
array('course_no' => 'PE 4' , 'color' => 'white'),
array('course_no' => 'HUMANITIES 1' , 'color' => 'white'),
array('course_no' => 'MATH 7' , 'color' => 'white')
)
),
'preqs' => array(
array('subject' => 'CS 120' , 'preq' => 'CS 110'),
array('subject' => 'CS 121' , 'preq' => 'CS 111'),
array('subject' => 'CS 122' , 'preq' => 'MATH 1'),
array('subject' => 'MATH 4' , 'preq' => 'MATH 1'),
array('subject' => 'MATH 4' , 'preq' => 'MATH 2'),
array('subject' => 'ENGL 2' , 'preq' => 'ENGL 1'),
array('subject' => 'REED 2' , 'preq' => 'REED 1'),
array('subject' => 'PE 2' , 'preq' => 'PE 1' ),
array('subject' => 'CWTS/ROTC 12', 'preq' => 'CWTS/ROTC 11'),
array('subject' => 'GUIDANCE 2' , 'preq' => 'GUIDANCE 1'),
array('subject' => 'CS 211' , 'preq' => 'CS 121'),
array('subject' => 'CS 212' , 'preq' => 'CS 110'),
array('subject' => 'CS 213' , 'preq' => 'CS 122'),
array('subject' => 'ENGL 3' , 'preq' => 'ENGL 2'),
array('subject' => 'MATH 6' , 'preq' => 'MATH 4'),
array('subject' => 'REED 3' , 'preq' => 'REED 2'),
array('subject' => 'PE 3' , 'preq' => 'PE 2'),
array('subject' => 'CS 221' , 'preq' => 'CS 122'),
array('subject' => 'CS 221' , 'preq' => 'CS 211'),
array('subject' => 'CS 222' , 'preq' => 'CS 211'),
array('subject' => 'CS 223' , 'preq' => 'CS 211'),
array('subject' => 'CS 224' , 'preq' => 'CS 121'),
array('subject' => 'ENGL 4' , 'preq' => 'ENGL 2'),
array('subject' => 'REED 4' , 'preq' => 'REED 3'),
array('subject' => 'PE 4' , 'preq' => 'PE 3'),
)
);
?>
<!DOCTYPE html>
<html>
<head>
<title>Swimlane</title>
<!-- Copyright 1998-2014 by Northwoods Software Corporation. -->
<link href="goSamples.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="go.js"></script>
<script id="code">
function highlightNode(node, color)
if (node === null) return;
var shape = node.findMainElement();
if (shape === null) return;
if (color !== undefined)
if (!shape.previousStroke) shape.previousStroke = shape.stroke;
shape.stroke = color;
else // restore previous color
shape.stroke = shape.previousStroke;
function highlightLink(link, color)
if (link === null) return;
var shape = link.findMainElement();
if (shape === null) return;
if (color !== undefined)
if (!shape.previousStroke) shape.previousStroke = shape.stroke;
shape.stroke = color;
else // restore previous color
shape.stroke = shape.previousStroke;
function highlightConnectedNodes(node, color)
if (node === null) return;
var lit = node.findLinksOutOf();
while (lit.next())
highlightLink(lit.value, color);
var nit = node.findNodesOutOf();
while (nit.next())
highlightNode(nit.value, color);
highlightConnectedNodes(nit.value, color);
// These parameters need to be set before defining the templates.
// this controls whether the swimlanes are horizontal stacked vertically, or the other way:
var HORIZONTAL = false;
// this controls the minimum length of any swimlane
var MINLENGTH = 500;
// this controls the minimum breadth of any swimlane
var MINBREADTH = 360;
// compute the minimum length needed to hold all of the subgraphs
function computeMinPlaceholderSize(diagram)
var len = MINLENGTH;
for (var it = diagram.nodes; it.next(); )
var group = it.value;
if (!(group instanceof go.Group))
continue;
var holder = group.placeholder;
if (holder !== null)
var sz = holder.actualBounds;
len = Math.max(len, (HORIZONTAL ? sz.width : sz.height));
return (HORIZONTAL ? new go.Size(len, NaN) : new go.Size(NaN, len));
// get the minimum placeholder size for a particular Group;
// when group is null, return the minimum size
function computePlaceholderSize(group)
if (group instanceof go.Group)
var holder = group.placeholder;
if (holder !== null)
return holder.actualBounds.size;
return (HORIZONTAL ? new go.Size(MINLENGTH, MINBREADTH) : new go.Size(MINBREADTH, MINLENGTH));
// define a custom grid layout that makes sure the length of each lane is the same
// and that each lane is broad enough to hold its subgraph
function StackLayout()
go.GridLayout.call(this);
go.Diagram.inherit(StackLayout, go.GridLayout);
StackLayout.prototype.doLayout = function(coll)
var diagram = this.diagram;
if (diagram === null)
return;
diagram.startTransaction("StackLayout");
// make sure all of the Group Shapes are big enough
var minsize = computeMinPlaceholderSize(diagram);
for (var it = diagram.nodes; it.next(); )
var group = it.value;
if (!(group instanceof go.Group))
continue;
var shape = group.findObject("SHAPE");
if (shape !== null) // change the desiredSize to be big enough in both directions
var sz = computePlaceholderSize(group);
if (HORIZONTAL)
shape.width = (isNaN(shape.width) ? minsize.width : Math.max(shape.width, minsize.width));
if (!isNaN(shape.height))
shape.height = Math.max(shape.height, sz.height);
else
if (!isNaN(shape.width))
shape.width = Math.max(shape.width, sz.width);
shape.height = (isNaN(shape.height) ? minsize.height : Math.max(shape.height, minsize.height));
var cell = group.resizeCellSize;
if (!isNaN(shape.width) && !isNaN(cell.width) && cell.width > 0)
shape.width = Math.ceil(shape.width / cell.width) * cell.width;
if (!isNaN(shape.height) && !isNaN(cell.height) && cell.height > 0)
shape.height = Math.ceil(shape.height / cell.height) * cell.height;
// now do all of the usual stuff, according to whatever properties have been set on this GridLayout
go.GridLayout.prototype.doLayout.call(this, coll);
diagram.commitTransaction("StackLayout");
;
// end StackLayout class
function init()
var $ = go.GraphObject.make;
myDiagram =
$(go.Diagram, "myDiagram",
initialContentAlignment: go.Spot.Center,
layout:
$(StackLayout,
cellSize: new go.Size(1, 1),
spacing: new go.Size(0, 0),
wrappingColumn: (HORIZONTAL ? 1 : Infinity),
wrappingWidth: Infinity,
isViewportSized: false
)
);
myDiagram.nodeTemplate =
$(go.Node, "Auto",
$(go.Shape, "Rectangle",
fill: "lightblue", portId: "", cursor: "pointer", fromLinkable: true, toLinkable: true ,
new go.Binding("fill", "color")),
$(go.TextBlock, margin: 5 ,
new go.Binding("text", "key")),
// limit dragging of Nodes to stay within the containing Group, defined above
mouseEnter: function(e, obj, prev)
var shape = obj.findMainElement();
if (shape)
shape.previousFill = shape.fill || "lightblue";
shape.fill = "white";
highlightConnectedNodes(obj, "red");
,
mouseLeave: function(e, obj, next)
var shape = obj.findMainElement();
var original_color = obj.wl.color;
if (shape) shape.fill = shape.previousFill;
highlightConnectedNodes(obj);
);
// each Group is a "swimlane" with a header on the left and a resizable lane on the right
myDiagram.groupTemplate =
$(go.Group, HORIZONTAL ? "Horizontal" : "Vertical",
movable: false, copyable: false, deletable: false, // can't move or copy or delete lanes
avoidable: false,
selectionObjectName: "SHAPE", // selecting a lane causes the body of the lane to be highlit, not the label
resizable: true, resizeObjectName: "SHAPE", // allow lanes to be resized, but the custom resizeAdornmentTemplate only permits one kind of resizing
layout: $(go.LayeredDigraphLayout, // automatically lay out the lane's subgraph
direction: HORIZONTAL ? 90 : 0, columnSpacing: 10, layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource ),
computesBoundsAfterDrag: false, // needed to prevent recomputing Group.placeholder bounds too soon
computesBoundsIncludingLinks: false,
computesBoundsIncludingLocation: true
,
// the lane header consisting of a Shape and a TextBlock
$(go.Panel, "Horizontal",
angle: HORIZONTAL ? 270 : 0, // maybe rotate the header to read sideways going up
alignment: go.Spot.Center
,
$(go.Shape, "Diamond",
width: 0, height: 0 ,
new go.Binding("fill", "color")),
$(go.TextBlock, // the lane label
font: "bold 14pt Century Gothic" ,
new go.Binding("text", "key"))
),
// end Horizontal Panel
$(go.Panel, "Auto", // the lane consisting of a background Shape and a Placeholder representing the subgraph
$(go.Shape, "Rectangle",
name: "SHAPE", fill: "white", minSize: computePlaceholderSize(null) ,
new go.Binding("fill", "color")),
$(go.Placeholder,
padding: 10, alignment: go.Spot.TopLeft )
)
// end Auto Panel
);
// end Group
// define a custom resize adornment that only has a single resize handle
myDiagram.groupTemplate.resizeAdornmentTemplate =
$(go.Adornment, "Spot",
$(go.Placeholder),
$(go.Shape, // for changing the length of a lane
alignment: HORIZONTAL ? go.Spot.Right: go.Spot.Bottom,
desiredSize: HORIZONTAL ? new go.Size(7, 50) : new go.Size(50, 7),
fill: "lightblue", stroke: "dodgerblue",
cursor: HORIZONTAL ? "col-resize" : "row-resize"
),
$(go.Shape, // for changing the breadth of a lane
alignment: HORIZONTAL ? go.Spot.Bottom : go.Spot.Right,
desiredSize: HORIZONTAL ? new go.Size(50, 7) : new go.Size(7, 50),
fill: "lightblue", stroke: "dodgerblue",
cursor: HORIZONTAL ? "row-resize" : "col-resize"
)
);
myDiagram.linkTemplate =
$(go.Link,
routing: go.Link.AvoidsNodes, corner: 5 ,
relinkableFrom: true, relinkableTo: true ,
$(go.Shape),
$(go.Shape, toArrow: "Standard" ),
layoutConditions: go.Part.LayoutAdded
);
// define some sample graphs in some of the lanes
myDiagram.model = new go.GraphLinksModel(
[ // node data
key: "First Year" , isGroup: true, color: "#84ff9e" ,
key: "Second Year" , isGroup: true, color: "#dafb69" ,
key: "Third Year" , isGroup: true, color: "#fd5c91" ,
key: "Fourth Year" , isGroup: true, color: "#6f5cfd" ,
<?php
if(isset($prospectus['subjects']))
if(!empty($prospectus['subjects']))
foreach($prospectus['subjects'] as $year => $subjects)
foreach($subjects as $subject)
echo 'key: "' . $subject['course_no'] . '", group: "' . $year . '", color: "' . $subject['color'] . '",' ;
?>
],
[ // link data
<?php
if(isset($prospectus['preqs']))
if(!empty($prospectus['preqs']))
foreach($prospectus['preqs'] as $preq)
echo "from: '" . $preq['preq'] . "', to: '" . $preq['subject'] . "'," ;
?>
]);
myDiagram.isReadOnly = true;
</script>
</head>
<body onload="init()" style="font-family: Century Gothic">
<div id="sample" style="margin-left: 0px">
<div id="myDiagram" style="border: solid 1px blue; width:100%; height:750px;">
</div>
</div>
</body>
</html>
【问题讨论】:
【参考方案1】:这里是包含鼠标进入和离开功能的节点模板部分:
myDiagram.nodeTemplate =
$(go.Node, "Auto",
$(go.Shape, "Rectangle",
fill: "lightblue", portId: "", cursor: "pointer", fromLinkable: true, toLinkable: true ,
new go.Binding("fill", "color")),
$(go.TextBlock, margin: 5 ,
new go.Binding("text", "key")),
// limit dragging of Nodes to stay within the containing Group, defined above
dragComputation: stayInGroup,
mouseDrop: function (e, node) // dropping a copy of some Nodes and Links onto this Node adds them to this Node's Group
if (!e.shift && !e.control) return; // cannot change groups with an unmodified drag-and-drop
var grp = node.containingGroup;
if (grp !== null)
var ok = grp.addMembers(node.diagram.selection, true);
if (!ok) grp.diagram.currentTool.doCancel();
,
layoutConditions: go.Part.LayoutAdded | go.Part.LayoutNodeSized,
mouseEnter: function(e, obj, prev)
var shape = obj.findMainElement();
if (shape)
shape.previousFill = shape.fill || "lightblue";
shape.fill = "white";
highlightConnectedNodes(obj, "red");
,
mouseLeave: function(e, obj, next)
var shape = obj.findMainElement();
var original_color = obj.wl.color;
if (shape) shape.fill = shape.previousFill;
highlightConnectedNodes(obj);
);
这两个函数都调用 highlightConnectedNodes 函数,该函数又调用另外两个函数。一个用于节点,一个用于链接。
function highlightNode(node, color)
if (node === null) return;
var shape = node.findMainElement();
if (shape === null) return;
if (color !== undefined)
if (!shape.previousStroke) shape.previousStroke = shape.stroke;
shape.stroke = color;
else // restore previous color
shape.stroke = shape.previousStroke;
function highlightLink(link, color)
if (link === null) return;
var shape = link.findMainElement();
if (shape === null) return;
if (color !== undefined)
if (!shape.previousStroke) shape.previousStroke = shape.stroke;
shape.stroke = color;
else // restore previous color
shape.stroke = shape.previousStroke;
function highlightConnectedNodes(node, color)
if (node === null) return;
var lit = node.findLinksInto();
while (lit.next())
highlightLink(lit.value, color);
var nit = node.findNodesInto();
while (nit.next())
highlightNode(nit.value, color);
highlightConnectedNodes(nit.value, color);
当您将鼠标悬停在某个节点上时,与其相连的所有节点和链接都会改变颜色(向后移动)。但是,如果您只希望悬停节点前面的节点和链接更改颜色,则可以更改
findNodesInto
findLinksInto
到
findNodesOutOf
findLinksOutOf
希望对您有所帮助。
【讨论】:
非常感谢@James 先生!我希望你很快还在外面,因为对于我们的最终项目,我仍然有很多与这个库有关的问题。 我只需要在这里发布这个作为后续问题。 先生,你能用你的代码绑定this吗?我已经实现了上面的代码并且运行良好(我喜欢它,谢谢!我只需要加厚链接,更改颜色等)。我真正想要添加的功能是当a节点悬停时,它还会显示其详细信息。 P.S.先生是否可以保留悬停节点的颜色?因为每当我将鼠标悬停在非白色节点上时,它就会在悬停时变为白色。【参考方案2】:如果您想在将鼠标悬停在节点上时突出显示节点,则很可能您真的想使用mouseEnter
和mouseLeave
。这是一个简单的例子:
var $ = go.GraphObject.make; // for conciseness in defining templates
var diagram = $(go.Diagram, "myDiagram", // create a Diagram for the DIV HTML element
initialContentAlignment: go.Spot.Center ); // center the content
diagram.initialContentAlignment = go.Spot.Center;
function mouseEnter(e, obj)
var shape = obj.findObject('SHAPE');
shape.fill = "#6DAB80";
shape.stroke = "#A6E6A1";
var text = obj.findObject('TEXT');
text.stroke = "white";
;
function mouseLeave(e, obj)
var shape = obj.findObject('SHAPE');
// Return the Shape's fill and stroke to the defaults
shape.fill = obj.data.color;
shape.stroke = null;
// Return the TextBlock's stroke to its default
var text = obj.findObject('TEXT');
text.stroke = "black";
;
diagram.nodeTemplate =
$(go.Node, "Auto",
mouseEnter: mouseEnter,
mouseLeave: mouseLeave
,
$(go.Shape, "Rectangle",
strokeWidth: 2, stroke: null, name: 'SHAPE' ,
new go.Binding("fill", "color")),
$(go.TextBlock,
margin: 10, font: "bold 18px Verdana", name: 'TEXT' ,
new go.Binding("text", "key"))
);
diagram.model = new go.GraphLinksModel(
[
key: "Alpha", color: "#96D6D9" ,
key: "Beta", color: "#96D6D9" ,
key: "Gamma", color: "#EFEBCA" ,
key: "Delta", color: "#EFEBCA"
],
[
from: "Alpha", to: "Beta" ,
from: "Alpha", to: "Gamma" ,
from: "Beta", to: "Beta" ,
from: "Gamma", to: "Delta" ,
from: "Delta", to: "Alpha"
]);
现场示例:http://jsfiddle.net/Y5sMN/
【讨论】:
谢谢先生,我知道这段代码会派上用场,我只需要用我的你来实现它。嗯……先生,可以像this这样吗?它表示显示的详细信息是有关悬停节点的信息。我还没有传递信息(来自控制器,因为我正在使用 CI 框架),所以如果你创建一个你的虚拟对象就可以了。 谢谢先生,但如果您能帮我用我的程序实现它,我将不胜感激 =)。先生? 先生,我在处理数据可视化代码时遇到了问题。因为据我所知,模型是通过细节本身创建的。我想要的是,除了我拥有的模型之外,我还想为细节创建另一个实体。 T_T 也许你有什么想法?以上是关于在每个节点上应用 onmouseover - GOjs 库 - 泳道的主要内容,如果未能解决你的问题,请参考以下文章