Browse Source

feat(network): Allow for image nodes to have a selected or broken image (#2601)

runTests
Guilhem Soulas 7 years ago
committed by Alexander Wunschik
parent
commit
66a67727ac
10 changed files with 216 additions and 15 deletions
  1. +17
    -3
      docs/network/nodes.html
  2. BIN
      examples/network/imageSelected/broken-image.png
  3. +82
    -0
      examples/network/imageSelected/imageSelected.html
  4. +53
    -0
      examples/network/imageSelected/selected.svg
  5. +6
    -0
      examples/network/imageSelected/unselected.svg
  6. +10
    -5
      lib/network/modules/components/Node.js
  7. +9
    -3
      lib/network/modules/components/nodes/shapes/CircularImage.js
  8. +10
    -2
      lib/network/modules/components/nodes/shapes/Image.js
  9. +24
    -1
      lib/network/modules/components/nodes/util/CircleImageBase.js
  10. +5
    -1
      lib/network/options.js

+ 17
- 3
docs/network/nodes.html View File

@ -734,14 +734,28 @@ network.setOptions(options);
<td><code>undefined</code></td> <td><code>undefined</code></td>
<td>The id of the node. The id is mandatory for nodes and they have to be unique. This should obviously be set per node, not globally.</td> <td>The id of the node. The id is mandatory for nodes and they have to be unique. This should obviously be set per node, not globally.</td>
</tr> </tr>
<tr>
<td>image</td>
<td>String</td>
<tr class='toggle collapsible' onclick="toggleTable('optionTable','image', this);">
<td><span parent="image" class="right-caret"></span> image</td>
<td>Object or String</td>
<td><code>undefined</code></td> <td><code>undefined</code></td>
<td>When the shape is set to <code>image</code> or <code>circularImage</code>, this option should be the URL <td>When the shape is set to <code>image</code> or <code>circularImage</code>, this option should be the URL
to an image. If the image cannot be found, the brokenImage option can be used. to an image. If the image cannot be found, the brokenImage option can be used.
</td> </td>
</tr> </tr>
<tr parent="image" class="hidden">
<td class="indent">image.unselected</td>
<td>String</td>
<td><code>undefined</code></td>
<td>Unselected (default) image URL.
</td>
</tr>
<tr parent="image" class="hidden">
<td class="indent">image.selected</td>
<td>String</td>
<td><code>undefined</code></td>
<td>Selected image URL.
</td>
</tr>
<tr> <tr>
<td>label</td> <td>label</td>
<td>String</td> <td>String</td>

BIN
examples/network/imageSelected/broken-image.png View File

Before After
Width: 256  |  Height: 256  |  Size: 1.7 KiB

+ 82
- 0
examples/network/imageSelected/imageSelected.html View File

@ -0,0 +1,82 @@
<html>
<head>
<title>Network | Selected/Unselected Image</title>
<script type="text/javascript" src="../../../dist/vis.js"></script>
<link href="../../../dist/vis-network.min.css" rel="stylesheet" type="text/css" />
<style type="text/css">
body {
font: 10pt arial;
}
#mynetwork {
width: 600px;
height: 600px;
border: 1px solid lightgray;
}
</style>
</head>
<body>
<div id="mynetwork"></div>
<script type="text/javascript">
// create an array with nodes
var nodes = new vis.DataSet([{
id: 1,
shape: 'image',
size: 20,
label: 'No select image',
image: './unselected.svg',
}, {
id: 2,
shape: 'image',
size: 20,
label: 'Select image broken',
image: {
unselected: './unselected.svg',
selected: './BROKEN_LINK/selected.svg',
},
}, {
id: 3,
shape: 'image',
size: 20,
label: 'Select works!',
image: {
unselected: './unselected.svg',
selected: './selected.svg',
},
shapeProperties: {
borderDashes: [15, 5],
interpolation: false,
}
}]);
// create an array with edges
var edges = new vis.DataSet([
{from: 1, to: 2},
{from: 2, to: 3},
]);
// create a network
var container = document.getElementById('mynetwork');
// provide the data in the vis format
var data = {
nodes: nodes,
edges: edges
};
var options = {
layout:{
randomSeed: 5
},
nodes: {
brokenImage: './broken-image.png',
}
};
// initialize!
var network = new vis.Network(container, data, options);
</script>
</body>
</html>

+ 53
- 0
examples/network/imageSelected/selected.svg View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
id="svg4137" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="504.566px"
height="577.672px" viewBox="0 0 504.566 577.672" enable-background="new 0 0 504.566 577.672" xml:space="preserve">
<g id="path4697_1_">
<polygon fill="#FFCB94" points="16,152.415 252.29,15.998 488.572,152.415 488.572,425.249 252.29,561.667 16.008,425.249 "/>
<g>
<polyline fill="none" stroke="#214255" stroke-width="40" stroke-miterlimit="100" points="16,152.425 16,152.415 16.009,152.41
"/>
<line fill="none" stroke="#214255" stroke-width="40" stroke-miterlimit="100" stroke-dasharray="68.2077,0,68.2077,68.2032" x1="75.075" y1="118.309" x2="222.748" y2="33.053"/>
<polyline fill="none" stroke="#214255" stroke-width="40" stroke-miterlimit="100" points="252.281,16.002 252.29,15.998
252.299,16.002 "/>
<line fill="none" stroke="#214255" stroke-width="40" stroke-miterlimit="100" stroke-dasharray="68.2058,0,68.2058,68.2013" x1="311.363" y1="50.104" x2="459.031" y2="135.36"/>
<polyline fill="none" stroke="#214255" stroke-width="40" stroke-miterlimit="100" points="488.563,152.41 488.572,152.415
488.572,152.425 "/>
<line fill="none" stroke="#214255" stroke-width="40" stroke-miterlimit="100" stroke-dasharray="68.2056,0,68.2056,68.2011" x1="488.572" y1="220.626" x2="488.572" y2="391.138"/>
<polyline fill="none" stroke="#214255" stroke-width="40" stroke-miterlimit="100" points="488.572,425.239 488.572,425.249
488.563,425.253 "/>
<line fill="none" stroke="#214255" stroke-width="40" stroke-miterlimit="100" stroke-dasharray="68.2059,0,68.2059,68.2014" x1="429.499" y1="459.354" x2="281.831" y2="544.611"/>
<polyline fill="none" stroke="#214255" stroke-width="40" stroke-miterlimit="100" points="252.299,561.662 252.29,561.667
252.281,561.662 "/>
<line fill="none" stroke="#214255" stroke-width="40" stroke-miterlimit="100" stroke-dasharray="68.2061,0,68.2061,68.2016" x1="193.218" y1="527.561" x2="45.549" y2="442.304"/>
<polyline fill="none" stroke="#214255" stroke-width="40" stroke-miterlimit="100" points="16.017,425.253 16.008,425.249
16.008,425.239 "/>
<line fill="none" stroke="#214255" stroke-width="40" stroke-miterlimit="100" stroke-dasharray="68.2056,0,68.2056,68.2011" x1="16.006" y1="357.038" x2="16.001" y2="186.525"/>
</g>
</g>
<g id="path4697">
<g>
<polyline fill="none" stroke="#F7941E" stroke-width="40" stroke-miterlimit="100" points="16.002,228.415 16,152.415
81.819,114.416 "/>
<polyline fill="none" stroke="#F7941E" stroke-width="40" stroke-miterlimit="100" points="186.472,53.997 252.29,15.998
318.108,53.998 "/>
<polyline fill="none" stroke="#F7941E" stroke-width="40" stroke-miterlimit="100" points="422.754,114.415 488.572,152.415
488.572,228.415 "/>
<polyline fill="none" stroke="#F7941E" stroke-width="40" stroke-miterlimit="100" points="488.572,349.249 488.572,425.249
422.754,463.249 "/>
<polyline fill="none" stroke="#F7941E" stroke-width="40" stroke-miterlimit="100" points="318.108,523.667 252.29,561.667
186.472,523.667 "/>
<polyline fill="none" stroke="#F7941E" stroke-width="40" stroke-miterlimit="100" points="81.826,463.249 16.008,425.249
16.006,349.249 "/>
</g>
</g>
</svg>

+ 6
- 0
examples/network/imageSelected/unselected.svg View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg id="svg4137" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="203.79mm" width="178mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 630.70347 722.08501">
<g id="layer1" transform="translate(61.066 -28.463)">
<path id="path4697" stroke-linejoin="round" fill="#f57f17" stroke="#37474e" stroke-linecap="round" stroke-miterlimit="100" stroke-width="40" d="m-41.066 218.98 295.36-170.52l295.35 170.52 0.00001 341.04-295.35 170.52-295.35-170.52z"/>
</g>
</svg>

+ 10
- 5
lib/network/modules/components/Node.js View File

@ -107,6 +107,7 @@ class Node {
if (!options) { if (!options) {
return; return;
} }
// basic options // basic options
if (options.id !== undefined) {this.id = options.id;} if (options.id !== undefined) {this.id = options.id;}
@ -114,7 +115,6 @@ class Node {
throw "Node must have an id"; throw "Node must have an id";
} }
// set these options locally // set these options locally
// clear x and y positions // clear x and y positions
if (options.x !== undefined) { if (options.x !== undefined) {
@ -144,7 +144,12 @@ class Node {
// load the images // load the images
if (this.options.image !== undefined) { if (this.options.image !== undefined) {
if (this.imagelist) { if (this.imagelist) {
this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage, this.id);
if (typeof this.options.image === 'string') {
this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage, this.id);
} else {
this.imageObj = this.imagelist.load(this.options.image.unselected, this.options.brokenImage, this.id);
this.imageObjAlt = this.imagelist.load(this.options.image.selected, this.options.brokenImage, this.id);
}
} }
else { else {
throw "No imagelist provided"; throw "No imagelist provided";
@ -295,7 +300,7 @@ class Node {
updateShape(currentShape) { updateShape(currentShape) {
if (currentShape === this.options.shape && this.shape) { if (currentShape === this.options.shape && this.shape) {
this.shape.setOptions(this.options, this.imageObj);
this.shape.setOptions(this.options, this.imageObj, this.imageObjAlt);
} }
else { else {
// choose draw method depending on the shape // choose draw method depending on the shape
@ -307,7 +312,7 @@ class Node {
this.shape = new Circle(this.options, this.body, this.labelModule); this.shape = new Circle(this.options, this.body, this.labelModule);
break; break;
case 'circularImage': case 'circularImage':
this.shape = new CircularImage(this.options, this.body, this.labelModule, this.imageObj);
this.shape = new CircularImage(this.options, this.body, this.labelModule, this.imageObj, this.imageObjAlt);
break; break;
case 'database': case 'database':
this.shape = new Database(this.options, this.body, this.labelModule); this.shape = new Database(this.options, this.body, this.labelModule);
@ -325,7 +330,7 @@ class Node {
this.shape = new Icon(this.options, this.body, this.labelModule); this.shape = new Icon(this.options, this.body, this.labelModule);
break; break;
case 'image': case 'image':
this.shape = new Image(this.options, this.body, this.labelModule, this.imageObj);
this.shape = new Image(this.options, this.body, this.labelModule, this.imageObj, this.imageObjAlt);
break; break;
case 'square': case 'square':
this.shape = new Square(this.options, this.body, this.labelModule); this.shape = new Square(this.options, this.body, this.labelModule);

+ 9
- 3
lib/network/modules/components/nodes/shapes/CircularImage.js View File

@ -1,12 +1,13 @@
'use strict'; 'use strict';
import CircleImageBase from '../util/CircleImageBase' import CircleImageBase from '../util/CircleImageBase'
class CircularImage extends CircleImageBase { class CircularImage extends CircleImageBase {
constructor (options, body, labelModule, imageObj) {
constructor (options, body, labelModule, imageObj, imageObjAlt) {
super(options, body, labelModule); super(options, body, labelModule);
this.imageObj = imageObj;
this.setImages(imageObj, imageObjAlt);
this._swapToImageResizeWhenImageLoaded = true; this._swapToImageResizeWhenImageLoaded = true;
} }
@ -31,6 +32,11 @@ class CircularImage extends CircleImageBase {
} }
draw(ctx, x, y, selected, hover, values) { draw(ctx, x, y, selected, hover, values) {
// switch images depending on 'selected' if imageObjAlt exists
if (this.imageObjAlt) {
this.switchImages(selected);
}
this.resize(); this.resize();
this.left = x - this.width / 2; this.left = x - this.width / 2;

+ 10
- 2
lib/network/modules/components/nodes/shapes/Image.js View File

@ -3,9 +3,10 @@
import CircleImageBase from '../util/CircleImageBase' import CircleImageBase from '../util/CircleImageBase'
class Image extends CircleImageBase { class Image extends CircleImageBase {
constructor (options, body, labelModule, imageObj) {
constructor (options, body, labelModule, imageObj, imageObjAlt) {
super(options, body, labelModule); super(options, body, labelModule);
this.imageObj = imageObj;
this.setImages(imageObj, imageObjAlt);
} }
resize() { resize() {
@ -13,6 +14,13 @@ class Image extends CircleImageBase {
} }
draw(ctx, x, y, selected, hover, values) { draw(ctx, x, y, selected, hover, values) {
// switch images depending on 'selected' if imageObjAlt exists
if (this.imageObjAlt) {
this.switchImages(selected);
}
this.selected = selected;
this.resize(); this.resize();
this.left = x - this.width / 2; this.left = x - this.width / 2;
this.top = y - this.height / 2; this.top = y - this.height / 2;

+ 24
- 1
lib/network/modules/components/nodes/util/CircleImageBase.js View File

@ -5,13 +5,36 @@ class CircleImageBase extends NodeBase {
super(options, body, labelModule); super(options, body, labelModule);
this.labelOffset = 0; this.labelOffset = 0;
this.imageLoaded = false; this.imageLoaded = false;
this.selected = false;
} }
setOptions(options, imageObj) {
setOptions(options, imageObj, imageObjAlt) {
this.options = options; this.options = options;
this.setImages(imageObj, imageObjAlt);
}
setImages(imageObj, imageObjAlt) {
if (imageObj) { if (imageObj) {
this.imageObj = imageObj; this.imageObj = imageObj;
if (imageObjAlt) {
this.imageObjAlt = imageObjAlt;
}
}
}
/**
* Switch between the base and the selected image.
*/
switchImages(selected) {
if ((selected && !this.selected) || (!selected && this.selected)) {
let imageTmp = this.imageObj;
this.imageObj = this.imageObjAlt;
this.imageObjAlt = imageTmp;
} }
// keep current state in memory
this.selected = selected;
} }
/** /**

+ 5
- 1
lib/network/options.js View File

@ -280,7 +280,11 @@ let allOptions = {
__type__: { object } __type__: { object }
}, },
id: { string, number }, id: { string, number },
image: { string, 'undefined': 'undefined' }, // --> URL
image: {
selected: { string, 'undefined': 'undefined' }, // --> URL
unselected: { string, 'undefined': 'undefined' }, // --> URL
__type__: { object, string }
},
label: { string, 'undefined': 'undefined' }, label: { string, 'undefined': 'undefined' },
labelHighlightBold: { boolean: bool }, labelHighlightBold: { boolean: bool },
level: { number, 'undefined': 'undefined' }, level: { number, 'undefined': 'undefined' },

Loading…
Cancel
Save