Anchors
An anchor models the notion of where on an element a connector should connect - it defines the location of an endpoint. It is a concept from the Community edition, where it is used in various places, such as the connect
and addEndpoint
methods, as well as in the defaults, but it's also a key part of the Toolkit edition, where it is used in port and edge definitions inside your views.
There are four main types of Anchors:
static - these are fixed to some point on an element and do not move. They can be specified using a string to identify one of the defaults that jsPlumb ships with, or an array describing the location (see below)
dynamic - these are lists of static anchors from which jsPlumb picks the most appropriate one each time a connection is painted. The algorithm used to determine the most appropriate anchor picks the one that is closest to the center of the other element in the connection. A future version of jsPlumb might support a pluggable algorithm to make this decision.
perimeter anchors - these are anchors that follow the perimeter of some given shape. They are, in essence, dynamic anchors whose locations are chosen from the perimeter of the underlying shape.
continuous anchors - These anchors are not fixed to any specific location; they are assigned to one of the four faces of an element depending on that element's orientation to the other element in the associated connection. Continuous anchors are slightly more computationally intensive than static or dynamic anchors because jsPlumb is required to calculate the position of every connection during a paint cycle, rather than just connections belonging to the element in motion.
#
Static AnchorsjsPlumb has nine default anchor locations you can use to specify where the connectors connect to elements: these are the four corners of an element, the center of the element, and the midpoint of each edge of the element:
Top
TopRight
Right
BottomRight
Bottom
BottomLeft
Left
TopLeft
Center
The AnchorLocations enumeration contains these values.
Each of these string representations is just a wrapper around the underlying array-based syntax [x, y, dx, dy]
, where x
and y
are coordinates in the interval [0,1]
specifying the position of the anchor, and dx
and dy
,which specify the orientation of the curve incident to the anchor, can have a value of 0, 1 or -1. For example, [0, 0.5, -1, 0]
defines a Left
anchor with a connector curve that emanates leftward from the anchor. Similarly, [0.5, 0, 0, -1]
defines a Top
anchor with a connector curve emanating upwards.
anchor:"Bottom"
or
anchor: AnchorLocations.Bottom
is identical to:
anchor:[ 0.5, 1, 0, 1 ]
#
Anchor OffsetsIn addition to supplying the location and orientation of an anchor, you can optionally supply two more parameters that define an offset in pixels from the given location. Here's the anchor specified above, but with a 50 pixel offset below the element in the y axis:
anchor:[ 0.5, 1, 0, 1, 0, 50 ]
#
Dynamic AnchorsThese are Anchors that can be positioned in one of a number of locations, choosing the one that is most appropriate each time something moves or is painted in the UI.
There is no special syntax for creating a Dynamic Anchor; you just provide an array of individual static anchor specifications, eg:
anchor:[ [ 0.2, 0, 0, -1 ], [ 1, 0.2, 1, 0 ], [ 0.8, 1, 0, 1 ], [ 0, 0.8, -1, 0 ] ]
Note that you can mix the types of these individual static anchor specifications:
anchor: [ [ 0.2, 0, 0, -1 ], [ 1, 0.2, 1, 0 ], "Top", "Bottom" ];
#
Default Dynamic AnchorjsPlumb provides a dynamic anchor called AutoDefault
that chooses from Top
, Right
, Bottom
and Left
:
anchor:"AutoDefault"
or with ES6/Typescript:
anchor: AnchorLocations.AutoDefault
These two nodes have an endpoint with an AutoDefault
anchor - drag them around and see how they choose from Top
, Left
, Bottom
and Right
depending on their orientation.
#
Location SelectionThe algorithm that decides which location to choose just calculates which location is closest to the center of the other element in the Connection. It is possible that future versions of jsPlumb could support more sophisticated choice algorithms, if the need arose.
#
Draggable ConnectionsjsPlumb locks the position of a dynamic anchor when you start to drag a connection from it, and unlocks it once the connection is either established or abandoned. At that point you may see the position of the dynamic anchor change, as jsPlumb optimises the connection.
#
Perimeter AnchorsThese are a form of dynamic anchor in which the anchor locations are chosen from the perimeter of some given shape. jsPlumb supports six shapes:
Circle
Ellipse
Triangle
Diamond
Rectangle
Square
Rectangle
and Square
are not, strictly speaking, necessary, since rectangular shapes are the norm in a web page. But they are included for completeness.
instance.connect({ source: someElement, target: someOtherElement, endpoint:"Dot", anchor:{ type:"Perimeter", options:{ shape:"Circle" } }})
In this example our anchor will travel around the path inscribed by a circle whose diameter is the width and height of the underlying element.
Note that the Circle
shape is therefore identical to Ellipse
, since it is assumed the underlying element will have equal width and height, and if it does not, you will get an ellipse. Rectangle
and Square
have the same relationship.
By default, jsPlumb approximates the perimeter with 60 anchor locations. You can change this, though:
instance.connect({ source: someElement, target: someOtherElement, endpoint:"Dot", anchor:{ type:"Perimeter", options:{ shape:"Circle", anchorCount:150 } }})
Obviously, the more points the smoother the operation. But also the more work your browser has to do.
#
Rotating a Perimeter AnchorYou can supply a rotation
value to a Perimeter anchor. Here's how you would use it in the Community edition:
jsPlumbInstance.connect({ source:"someDiv", target:"someOtherDiv", endpoint:"Dot", anchors:[ { type:"Perimeter", options:{ shape:"Triangle", rotation:25 } }, { type:"Perimeter", options:{ shape:"Triangle", rotation:-335 } } ]});
Note that the value must be supplied in degrees, not radians, and the number may be either positive or negative. In the example above, both triangles are of course rotated by the same amount.
#
Continuous AnchorsAs discussed above, these are anchors whose positions are calculated by jsPlumb according to the orientation between elements in a Connection, and also how many other Continuous anchors happen to be sharing the element. You specify that you want to use Continuous anchors using the string syntax you would use to specify one of the default Static Anchors, for example:
instance.connect({ source:someDiv, target:someOtherDiv, anchor:"Continuous"});
Note in this example I specified only "anchor", rather than "anchors" - jsPlumb will use the same spec for both anchors. But I could have said this:
instance.connect({ source:someDiv, target:someOtherDiv, anchors:["Bottom", "Continuous"]});
...which would have resulted in the source element having a static anchor at BottomCenter
. In practise, though, it seems the continuous anchors work best if both elements in a connection are using them.
Note also that continuous anchors can be specified on addEndpoint
calls:
instance.addEndpoint(someDiv, { anchor:"Continuous", paintStyle:{ fill:"red" }});
... and in the jsPlumb defaults:
instance.importDefaults(anchor:"Continuous"})
Try dragging these nodes around and see how the anchors adapt their positions:
#
Choosing faces for a Continuous AnchorBy default, a Continuous anchor will choose points from all four faces of the element on which it resides. You can control this behaviour, though, with the faces
parameter on the anchor spec:
jsPlumbInstance.addEndpoint(someDiv, { anchor:{ type:"Continuous", options:{ faces:[ "top", "left" ] } }})
Allowed values are:
top
left
right
bottom
If you provide an empty array for the faces
parameter, jsPlumb will default to using all four faces.
#
Associating CSS classes with AnchorsThe array syntax discussed above supports an optional 7th value, which is a string that represents a CSS class. This CSS class is then associated with the anchor, and applied to the anchor's endpoint and element whenever the anchor is selected.
A static Anchor is of course always "selected", but a dynamic anchor cycles through a number of different locations, and each of these may have a different CSS class associated with it.
The CSS class that gets written to the endpoint and element is prefixed with the associated jsPlumb instance's endpointAnchorClass
prefix, which defaults to:
jtk-endpoint-anchor-
So if you had the following, for example:
const ep = jsPlumb.addEndpoint(someElement, { anchor:[0.5, 0, 0, -1, 0, 0, "top" ]};
Then the endpoint created by jsPlumb and also the element someDiv
would have this class assigned to them:
jtk-endpoint-anchor-top
An example using dynamic anchors:
const ep = jsPlumb.addEndpoint(someElement, { anchor:[ [ 0.5, 0, 0, -1, 0, 0, "top" ], [ 1, 0.5, 1, 0, 0, 0, "right" ] [ 0.5, 1, 0, 1, 0, 0, "bottom" ] [ 0, 0.5, -1, 0, 0, 0, "left" ] ]});
Here, the class assigned to the endpoint and element would cycle through these values as the anchor location changes:
jtk-endpoint-anchor-topjtk-endpoint-anchor-rightjtk-endpoint-anchor-leftjtk-endpoint-anchor-bottom
Note that if you supply a class name that consists of more than one term, jsPlumb will not prepend the prefix to each term in the class:
const ep = jsPlumb.addEndpoint(someElement, { anchor:[ 0.5, 0, 0, -1, 0, 0, "foo bar" ]});
would result in 2 classes being added to the Endpoint and Element:
jtk-endpoint-anchor-foo
and
bar