This is the sixth article of the intermediate section of the JointJS tutorial. Return to custom links. See index of basic and intermediate articles.
The basic tutorial series offered an introduction into link labels. This section explains everything you need to know to make full use of the powerful label system.
JointJS offers a full suite of methods for working with link labels:
link.labels(labels)
- sets the labels
array of the link.
If called without arguments, returns the labels
array.link.label(index, label)
- sets the provided label
at the given index
.
If called with index
only, returns that label.link.insertLabel(index, label)
- inserts the provided label
at the given index
.link.appendLabel(label)
- shortcut function.
Inserts the label
at the end of the labels
array.link.removeLabel(index)
- removes the label at index
.A simple label
definition (including markup, attrs and position) is built into the joint.dia.Link
class, from which all Link subtypes inherit it (including joint.shapes.standard.Link
).
The built-in default label markup contains two subelements:
<text>
SVGElement ('text'
selector) for label text, and
<rect>
SVGElement ('rect'
selector) for label background.
The built-in default attributes specify a simple vertical-centered text on a white rounded rectangle.
Finally, the built-in default position places the label at the midpoint of the link.
Thus, adding a label can be as simple as passing a value for the text/text
attribute:
link.appendLabel({
attrs: {
text: {
text: 'Hello, World!'
}
}
});
JointJS source code: links-label-builtin.js
The full built-in default label definition can be found in the documentation.
You can find a detailed overview of all label position properties in our documentation.
Labels are positioned at the center point of the link (distance
of 0.5
) as a
built-in default.
Three kinds of label.position.distance
values are recognized for setting a custom position.
A value between 0
and 1
causes the label to be positioned relatively to link length.
Positive values signify absolute position in local SVG units away from the start point.
Finally, negative values mean absolute position away from end point.
An animated example is presented below.
(Link labels can also be emulated with link
subelements and special attributes; this technique is explained elsewhere in the tutorial).
link.appendLabel({
attrs: {
text: {
text: '0.25'
}
},
position: {
distance: 0.25
}
});
link.appendLabel({
attrs: {
text: {
text: '150'
}
},
position: {
distance: 150
}
});
link.appendLabel({
attrs: {
text: {
text: '-100'
}
},
position: {
distance: -100
}
});
JointJS source code: link-labels-distance.js
Note that a label.position.distance
value is required for all labels.
If a label.position.distance
value is not provided for an individual label, the
defaultLabel.position.distance
value is taken from the
definition of the Link subtype.
If the Link subtype definition has no defaultLabel
object, or if its
defaultLabel
has no position
property, the
default built-in
value of 0.5
is applied.
Note that - if necessary - the default built-in position.distance
value will be mixined
with the rest of the custom position
object you provide (offset
,
angle
, args
).
It is also possible to set label offsets.
This is done with the label.position.offset
property.
With a positive number, the label is offset relatively and to the right of the link (according to the
source
-target
direction of the link); a negative number causes the label to be
offset to the left.
An object with x
and y
coordinates offsets the label absolutely by that amount
in the two dimensions.
The following example illustrates these three options.
The red asterisk marks the reference point of all labels on the link.
link.appendLabel({
attrs: {
text: {
text: 'offset: 40'
}
},
position: {
distance: 0.66,
offset: 40
}
});
link.appendLabel({
attrs: {
text: {
text: 'offset: -40'
}
},
position: {
distance: 0.66,
offset: -40
}
});
link.appendLabel({
attrs: {
text: {
text: 'offset: -40,80'
}
},
position: {
distance: 0.66,
offset: {
x: -40,
y: 80
}
}
});
JointJS source code: link-labels-offset.js
By default, the labels' anchor point is centered horizontally and vertically, as it was in this example.
This can be changed by the native textAnchor
SVG attribute and by the JointJS special
textVerticalAnchor
attribute, respectively.
Link labels are horizontal by default, but JointJS allows you to specify label rotation.
If you provide a value for the label.position.angle
property, the link will rotate clockwise by that amount (regardless of the path of the link).
If the label.position.args.keepGradient
boolean flag is set to true
, the label is first rotated so that its slope matches the slope of the connection path at the given position, and then the label is rotated further according to the label.position.angle
property (if any).
Additionally, if the label.position.args.ensureLegibility
boolean flag is set to true
, it ensures that label text never ends up being upside-down - if necessary, JointJS adds an additional 180-degree rotation to make the text legible.
The following example shows rotated links in action.
The red asterisk marks the reference point of the two labels that are offset from the connection path.
link.appendLabel({
attrs: {
text: {
text: '70°\nkeepGradient'
}
},
position: {
distance: 0.05,
angle: 70,
args: {
keepGradient: true
}
}
});
link.appendLabel({
attrs: {
text: {
text: '0°\nkeepGradient'
}
},
position: {
distance: 0.3,
args: {
keepGradient: true
}
}
});
link.appendLabel({
attrs: {
text: {
text: '45°'
}
},
position: {
distance: 0.8,
angle: 45
}
});
link.appendLabel({
attrs: {
text: {
text: '135°'
}
},
position: {
distance: 0.9,
angle: 135
}
});
link.appendLabel({
attrs: {
text: {
text: '270°\nkeepGradient'
}
},
position: {
distance: 0.66,
offset: 80,
angle: 270,
args: {
keepGradient: true
}
}
});
link.appendLabel({
attrs: {
text: {
text: '270°\nkeepGradient\nensureLegibility'
}
},
position: {
distance: 0.66,
offset: -80,
angle: 270,
args: {
keepGradient: true,
ensureLegibility: true
}
}
});
JointJS source code: link-labels-rotation.js
Of course, it is also possible to change the appearance of your labels.
To specify custom markup, you may
provide a markup
object in JSON format or as a joint.util.svg
ES6 tag template.
As a bonus, you can define custom selectors to identify individual components of your label.
To specify custom attributes, you
may provide an attrs
object with native SVG attributes or
JointJS special attributes.
Finally, to specify custom size of the
label, you may provide a size
object with width and/or height properties.
Let's define a complex circular label that shows what JointJS can do:
link.appendLabel({
markup: [
{
tagName: 'circle',
selector: 'body'
}, {
tagName: 'text',
selector: 'label'
}, {
tagName: 'circle',
selector: 'asteriskBody'
}, {
tagName: 'text',
selector: 'asterisk'
}
],
// no `size` object provided = calc() operations need `ref` property
attrs: {
label: {
text: '½',
fill: '#000000',
fontSize: 14,
textAnchor: 'middle',
yAlignment: 'middle',
pointerEvents: 'none'
},
body: {
// calc() is responsive to size of 'label':
ref: 'label',
fill: '#ffffff',
stroke: '#000000',
strokeWidth: 1,
r: 'calc(s)',
cx: 0,
cy: 0
},
asterisk: {
// calc() is responsive to size of 'label':
ref: 'label',
text: '*',
fill: '#ff0000',
fontSize: 8,
textAnchor: 'middle',
textVerticalAnchor: 'middle',
pointerEvents: 'none',
x: 'calc(x+16.5)',
y: 'calc(y-2)'
},
asteriskBody: {
// calc() is responsive to size of 'asterisk':
ref: 'asterisk',
fill: '#ffffff',
stroke: '#000000',
strokeWidth: 1,
r: 'calc(s)',
cx: 'calc(x+calc(0.5*w))',
cy: 'calc(y+calc(0.5*h))'
}
}
});
JointJS source code: link-labels-styling.js
We did not specify any size
object in our example, so JointJS was missing overarching
reference width and height dimensions for use in the various calc()
operations inside the
attrs
object.
In order to use calc()
operations anyways, we instead provided ref
special attributes where necessary, to
identify reference subelements from which each attrs
property could determine its
dimensions.
In our example, attrs/body
and attrs/asterisk
used the dimensions of the
label
SVGTextElement for reference, while attrs/asteriskBody
used the
dimensions of the asterisk
SVGTextElement for reference.
(If we had specified neither size
nor an individual ref
attribute within the
attrs
objects, the defaultLabel.size
from the definition of the Link subtype
would have been used for reference instead.
However, if there had been no defaultLabel
object in the Link subtype definition, or if its
defaultLabel
had had no size
property, the calculations would have used
0
as the reference width and height, which would have been unexpected.)
Additionally, note that in our example the x
and y
position of the
body
subelement is also determined by reference to the label
subelement - via
calc()
operations which
refer to the x
and y
variables - as is the case for the
asteriskBody
subelement which calculates its position by reference to the
asterisk
subelement.
By default, users cannot interact with link labels in any way.
However, you can enable label dragging for all labels with the paper.options.interactive
paper option:
var paper = new joint.dia.Paper({
// ...
interactive: {
linkMove: false,
labelMove: true,
arrowheadMove: false,
vertexMove: false,
vertexAdd: false,
vertexRemove: false,
useLinkTools: false
}
});
JointJS source code: link-labels-interaction.js
The new position of dragged labels is recorded relatively to the link path. That means that the label will reposition itself when the link changes. (Unfortunately, it also means that the label can never be dragged beyond the endpoints of the link).
If you only want to allow the label to be dragged along the length of the link (and not outside of it), you can do so by specifying the paper.options.snapLabels
paper option:
var paper = new joint.dia.Paper({
// ...
snapLabels: true,
interactive: {
linkMove: false,
labelMove: true,
arrowheadMove: false,
vertexMove: false,
vertexAdd: false,
vertexRemove: false,
useLinkTools: false
}
});
JointJS source code: link-labels-interaction-snap-labels.js
In the next section of the intermediate tutorial, we will learn about element tools.