Skip to main content

2 posts tagged with "position"

View All Tags

· 5 min read
Simon Porritt

Version 5.4.0 of the Community edition has support for a new concept - the ability to specify at drag start/stop what the location of an anchor should be, discussed in this post. This functionality is exposed to users of the Toolkit edition by supporting anchorPositionFinder values in the view.

Example#

This demonstration exhibits the same behaviour shown in the Community edition demonstration linked above: when you drop a connection on the right hand side of an element, its anchor is on the right face; when you drop on the left hand side, the anchor is on the left face.

Anchor position finders

These are the render params used to create this:

{  layout:{    type:"Absolute"  },  defaults:{    anchor:"Continuous",    endpoint:"Dot"  },  zoomToFit:true,  templates:{        "jtk-template-default":'<div data-jtk-source="true" data-jtk-target="true" data-jtk-target-port-type="twosides"></div>'  },  view:{    ports:{        twosides:{            anchorPositionFinder:function(el, elxy, vertex) {                                var x = elxy.x < 0.5 ? 0 : 1,                    ox = x === 0 ? -1 : 1;                                                    return [x, 0.5, ox, 0]            }           }    }      }

Aside from the anchorPositionFinder function itself, the key piece to grok in this setup is the way the node template is hooked up to the view. This is the html for each node:

<div data-jtk-source="true"         data-jtk-target="true"         data-jtk-target-port-type="twosides">        </div>

So we've got three attributes declared here:

  • data-jtk-source="true" Indicates the node is a connection drag source
  • data-jtk-target="true" Indicates the node is a connection drag target
  • data-jtk-target-port-type="twosides" Tells the Toolkit that this target should use the port definition called twosides, which is where our anchorPositionFinder is declared. Note that in the Toolkit you can make use of logical port mappings like this without actually declaring that the target is a port on the node. In this case, the node itself is still the target, but we're using the twosides port definition to define some behaviour.
note

In this demonstration we don't declare a node type; instead we use jtk-template-default as the ID of the template we provide. In the absence of any other information about how to render a node, the Toolkit will look for a template with that ID.

Another Example#

To illustrate the flexibility of this arrangement, let's create something more complex. We'll have three node types - red, blue and green.

  • red nodes will just use the default anchors (Continuous)
  • green nodes will use the anchor strategy from the first example
  • blue nodes will place an anchor exactly where the connection was dropped.
Anchor position finders - complex example

The render params for this example are as follows:

{  layout:{    type:"Absolute"  },  defaults:{    anchor:"Continuous",    endpoint:"Dot"  },  zoomToFit:true,  view:{    nodes:{        red:{            template:'<div data-jtk-source="true" data-jtk-target="true" style="background-color:red"></div>'        },        green:{          template:'<div data-jtk-source="true" data-jtk-target="true" style="background-color:mediumseagreen"></div>',          anchorPositionFinder:function(el, elxy, vertex) {                              var x = elxy.x < 0.5 ? 0 : 1,                  ox = x === 0 ? -1 : 1;                                                return [x, 0.5, ox, 0]          }        },        blue:{            template:'<div data-jtk-source="true" data-jtk-target="true" style="background-color:cadetblue"></div>',            anchorPositionFinder:function(el, elxy, vertex) {                                return [elxy.x, elxy.y, 0, 0]            }        }        }      }}

Here, we map a template for each node type, and then for blue and green types, we provide an anchorPositionFinder. The logic in the finder for green is as shown in the example above. For the blue node type, recall that elxy is the proportional location of the drop event on the drop element. This coordinate system is the same that Anchors use, so we can just return the values from elxy directly.

Note also that in this example we have declared anchorPositionFinder on node definitions, whereas in the previous example we declared the position finder on a port definition. You can define them on any vertex definition - node, port or group.

note

If you're new to the Toolkit and you'd like to know more about views, you can read about them in the documentation

· 4 min read
Simon Porritt

Version 5.4.0 of the Community edition, which was released today, has support for a new concept - the ability to specify at drag start/stop what the location of an anchor should be. In actual fact this concept is not entirely new: there was, in 2.x, the concept of an Assign anchor, which worked in a similar way, but this did not get ported to 5.x, and anchor position finders are much more straightforward to work with.

Example#

The best way to illustrate this is with an example. You can drag a connection from either box below. Where you drop the connection, though, has a bearing on the target anchor's position: if you drop it on the left hand side of the target element, the anchor will be positioned on the left. If you drop it on the right hand side, the anchor will be positioned on the right.

Anchor Position Finders
The code to make this happen looks like this:
import { newInstance } from "@jsplumb/browser-ui"  const instance = newInstance({    anchor: "Continuous",    endpoint: "Dot"})
instance.addSourceSelector("[data-jtk-managed]",{})
instance.addTargetSelector({   "[data-jtk-managed]":{       anchorPositionFinder:function(el, elxy) {           var x = elxy.x < 0.5 ? 0 : 1,               ox = x === 0 ? -1 : 1;                          return [x, 0.5, ox, 0]       }   }})

We create a new instance and give it defaults for anchor and endpoint, then add a source and target selector to it. Note that as a result of the selector we use for the source - [data-jtk-managed] - the elements end up not draggable in the demonstration, because that selector identifies the entire element, and when you register a source selector on an instance of jsPlumb, that selector is automatically added to the instance's drag filter.

The anchorPositionFinder in the target selector is where we make our decision about the location of the target anchor:

anchorPositionFinder:function(el, elxy) {   var x = elxy.x < 0.5 ? 0 : 1,       ox = x === 0 ? -1 : 1;          return [x, 0.5, ox, 0]}

el is the DOM element on which the connection is being dropped, and elxy is a PointXY object whose values are the proportional location of the drop event on the target element. For instance, this value:

{ x:0;5, y:0.5 }

would mean that the drop event occurred in the center of the element. In our position finder, set x=0 if elxy.x is less than 0.5, and 1 otherwise (I could have used Math.round(..) but I wanted to make it more explicit).

Another Example#

This also works when adding a source selector. Consider this code:

import { newInstance } from "@jsplumb/browser-ui"  const instance = newInstance({    endpoint: "Dot"})
instance.addSourceSelector("[data-jtk-managed]",{    anchorPositionFinder:function(el, elxy) {       var x = Math.round(elxy.x),            y = Math.round(elxy.y)                  return [x, y, 0, 0]   }})
instance.addTargetSelector({   "[data-jtk-managed]":{       anchorPositionFinder:function(el, elxy) {           return [0.5, 0.5, 0, 0]       }   }})

When we run this, we get a setup where the source anchor ends up at the corner of the quadrant from which you dragged:

Anchor Position Finders

The target anchor always ends up in the middle, because the anchor position finder on the target selector only ever returns an anchor located at the center.