Skip to main content

Link subelement labels

Special attributes also allow us to create custom labeling subelements - both those that adjust their rotation according to the underlying link path and those that always keep the same angle. However, keep in mind that link subelements are more difficult to set up and use dynamically than link labels (JointJS does not come with a built-in API to do this), and that they have a very important limitation when used this way:

  • They cannot use the ref attribute. This means that they are unable to automatically set their width and height values based on the browser-calculated dimensions of the text bbox. It is up to the programmer to provide approximate width and height values to accommodate the label text.

For this reason, we recommend everyone to use the link label API.

Link subelement labels that do not keep link connection gradient are enabled with the following two special attributes:

  • atConnectionLengthIgnoreGradient - sets the absolute distance from the beginning of the path at which the anchor of the subelement should be placed. Negative distances are counted from the end of the connection path. Does not rotate subelement according to path gradient.
  • atConnectionRatioIgnoreGradient - sets the relative distance from the beginning of the path at which the anchor of the subelement should be placed. Accepts numbers between 0 and 1. Does not rotate subelement according to path gradient.
note

(Link relative position atConnectionLength/atConnectionRatio attributes are actually aliases for more verbose names atConnectionLengthKeepGradient/atConnectionRatioKeepGradient.) In both cases (that is, whether we keep link gradient or not), offsetting of link subelement labels is achieved by clever use of the native x and y SVG attributes.

Let us illustrate with the following demo. A custom link type is defined with link label subelements positioned in such a way as to emulate the functionality shown in the link label position and offset examples. The red asterisk marks the reference point of the offset subelements on the link connection path.

import { dia } from '@joint/core';

const CustomLink = dia.Link.define('examples.CustomLink', {
attrs: {
line: {
connection: true,
fill: 'none',
stroke: '#333333',
strokeWidth: 2,
strokeLinejoin: 'round',
targetMarker: {
'type': 'path',
'd': 'M 10 -5 0 0 10 5 z'
}
},
relativeLabel: {
textAnchor: 'middle',
textVerticalAnchor: 'middle',
fill: 'black',
fontSize: 12
},
relativeLabelBody: {
x: -15,
y: -10,
width: 30,
height: 20,
fill: 'white',
stroke: 'black'
},
absoluteLabel: {
textAnchor: 'middle',
textVerticalAnchor: 'middle',
fill: 'black',
fontSize: 12
},
absoluteLabelBody: {
x: -15,
y: -10,
width: 30,
height: 20,
fill: 'white',
stroke: 'black'
},
absoluteReverseLabel: {
textAnchor: 'middle',
textVerticalAnchor: 'middle',
fill: 'black',
fontSize: 12
},
absoluteReverseLabelBody: {
x: -15,
y: -10,
width: 30,
height: 20,
fill: 'white',
stroke: 'black'
},
offsetLabelPositive: {
textAnchor: 'middle',
textVerticalAnchor: 'middle',
fill: 'black',
fontSize: 12
},
offsetLabelPositiveBody: {
width: 120,
height: 20,
fill: 'white',
stroke: 'black'
},
offsetLabelNegative: {
textAnchor: 'middle',
textVerticalAnchor: 'middle',
fill: 'black',
fontSize: 12
},
offsetLabelNegativeBody: {
width: 120,
height: 20,
fill: 'white',
stroke: 'black'
},
offsetLabelAbsolute: {
textAnchor: 'middle',
textVerticalAnchor: 'middle',
fill: 'black',
fontSize: 12
},
offsetLabelAbsoluteBody: {
width: 140,
height: 20,
fill: 'white',
stroke: 'black'
}
}
}, {
markup: [{
tagName: 'path',
selector: 'line'
}, {
tagName: 'rect',
selector: 'relativeLabelBody'
}, {
tagName: 'text',
selector: 'relativeLabel'
}, {
tagName: 'rect',
selector: 'absoluteLabelBody'
}, {
tagName: 'text',
selector: 'absoluteLabel'
}, {
tagName: 'rect',
selector: 'absoluteReverseLabelBody'
}, {
tagName: 'text',
selector: 'absoluteReverseLabel'
}, {
tagName: 'rect',
selector: 'offsetLabelPositiveBody'
}, {
tagName: 'text',
selector: 'offsetLabelPositive'
}, {
tagName: 'rect',
selector: 'offsetLabelNegativeBody'
}, {
tagName: 'text',
selector: 'offsetLabelNegative'
}, {
tagName: 'rect',
selector: 'offsetLabelAbsoluteBody'
}, {
tagName: 'text',
selector: 'offsetLabelAbsolute'
}]
});

const link = new CustomLink();
link.attr({
relativeLabel: {
atConnectionRatio: 0.25,
text: '0.25'
},
relativeLabelBody: {
atConnectionRatio: 0.25
},
absoluteLabel: {
atConnectionLength: 150,
text: '150'
},
absoluteLabelBody: {
atConnectionLength: 150
},
absoluteReverseLabel: {
atConnectionLength: -100,
text: '-100'
},
absoluteReverseLabelBody: {
atConnectionLength: -100
},
offsetLabelPositive: {
atConnectionRatio: 0.66,
y: 40,
text: 'keepGradient: 0,40'
},
offsetLabelPositiveBody: {
atConnectionRatio: 0.66,
x: -60, // 0 + -60
y: 30 // 40 + -10
},
offsetLabelNegative: {
atConnectionRatio: 0.66,
y: -40,
text: 'keepGradient: 0,-40'
},
offsetLabelNegativeBody: {
atConnectionRatio: 0.66,
x: -60, // 0 + -60
y: -50 // -40 + -10
},
offsetLabelAbsolute: {
atConnectionRatioIgnoreGradient: 0.66,
x: -40,
y: 80,
text: 'ignoreGradient: -40,80'
},
offsetLabelAbsoluteBody: {
atConnectionRatioIgnoreGradient: 0.66,
x: -110, // -40 + -70
y: 70 // 80 + -10
}
});