问题
I've done the first part, adding colors on each node. The next step would be how to add onmouseover on each node then highlight/apply style on the subgraph (including the links/lines connecting it) belonging to the hovered node.
Anyone knows?
This is my code so far:
<?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:
Here is the node template part containing the mouse enter and leave functions:
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);
}
}
);
Both functions call highlightConnectedNodes function which in turn calls two other functions. One for nodes and one for links.
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);
}
}
When you hover over a node all nodes and links connected to it change color (going backwards). However, if you want only nodes and links forward of the hovered node to change color you can change
findNodesInto
findLinksInto
to
findNodesOutOf
findLinksOutOf
Hope that helps.
回答2:
Most likely if you want to highlight nodes as you mouse over them, you really want to use mouseEnter
and mouseLeave
. Here's a simple example:
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" }
]);
Live example: http://jsfiddle.net/Y5sMN/
来源:https://stackoverflow.com/questions/22677378/apply-onmouseover-on-each-node-gojs-library-swimlane