How can I keep jointjs cells from overflowing the paper?
Asked Answered
C

4

13

I'm using jointjs to make diagrams which will be user-editable. The user may drag them around and relocate each cell. However, when a cell is dragged to the edge, it overflows and becomes cut off. I want to prevent this from happening, instead the cell to stop before it gets to the edge of the paper and not be allowed to cross the edge, thus always staying completely within the paper. The behavior can be seen in jointjs' very own demos here:

http://www.jointjs.com/tutorial/ports

Try dragging the cell to the edge and you'll see that it eventually becomes hidden as it crosses the edge of the paper element.

Secondly, I'm using the plugin for directed graph layout, found here:

http://jointjs.com/rappid/docs/layout/directedGraph

As you can see, the tree position automatically moves to the upper left of the paper element whenever your click layout. How can I modify these default positions? The only options I see for the provided function are space between ranks and space between nodes, no initial position. Say I wanted the tree to appear in the middle of the paper upon clicking 'layout', where would I have to make changes? Thanks in advance for any help.

Counteraccusation answered 5/5, 2015 at 20:5 Comment(0)
S
6

I think my previous answer is still feasible, but this is how I implemented it in my project. It has an advantage over the other answer in that it doesn't require you to use a custom elementView and seems simpler (to me).

(Working jsfiddle: http://jsfiddle.net/pL68gs2m/2/)

On the paper, handle the cell:pointermove event. In the event handler, work out the bounding box of the cellView on which the event was triggered and use that to constrain the movement.

var graph = new joint.dia.Graph;

var width = 400;
var height = 400;
var gridSize = 1;

var paper = new joint.dia.Paper({
    el: $('#paper'),
    width: width,
    height: height,
    model: graph,
    gridSize: gridSize
});

paper.on('cell:pointermove', function (cellView, evt, x, y) {

    var bbox = cellView.getBBox();
    var constrained = false;

    var constrainedX = x;

    if (bbox.x <= 0) { constrainedX = x + gridSize; constrained = true }
    if (bbox.x + bbox.width >= width) { constrainedX = x - gridSize; constrained = true }

    var constrainedY = y;

    if (bbox.y <= 0) {  constrainedY = y + gridSize; constrained = true }
    if (bbox.y + bbox.height >= height) { constrainedY = y - gridSize; constrained = true }

    //if you fire the event all the time you get a stack overflow
    if (constrained) { cellView.pointermove(evt, constrainedX, constrainedY) }
});
Strontian answered 22/6, 2015 at 21:35 Comment(1)
If it works for you could you unaccept myprevious answer? I think it would be better if people visiting this question in the future saw the second answer first :)Strontian
B
13

As an addition to Roman's answer, restrictTranslate can also be configured as true to restrict movement of elements to the boundary of the paper area.

Example:

var paper = new joint.dia.Paper({
    el: $('#paper'),
    width: 600,
    height: 400,
    model: graph,
    restrictTranslate: true
})
Buckskins answered 24/8, 2016 at 10:52 Comment(0)
K
7

I. To prevent elements from overflowing the paper you might use restrictTranslate paper option (JointJS v0.9.7+).

paper.options.restrictTranslate = function(cellView) {
    // move element inside the bounding box of the paper element only
    return cellView.paper.getArea();
}

http://jointjs.com/api#joint.dia.Paper:options

II. Use marginX and marginY DirectedGraph layout options to move the left-top corner of the resulting graph i.e. add margin to the left and top.

http://jointjs.com/rappid/docs/layout/directedGraph#configuration

Koh answered 9/5, 2016 at 7:27 Comment(0)
S
6

I think my previous answer is still feasible, but this is how I implemented it in my project. It has an advantage over the other answer in that it doesn't require you to use a custom elementView and seems simpler (to me).

(Working jsfiddle: http://jsfiddle.net/pL68gs2m/2/)

On the paper, handle the cell:pointermove event. In the event handler, work out the bounding box of the cellView on which the event was triggered and use that to constrain the movement.

var graph = new joint.dia.Graph;

var width = 400;
var height = 400;
var gridSize = 1;

var paper = new joint.dia.Paper({
    el: $('#paper'),
    width: width,
    height: height,
    model: graph,
    gridSize: gridSize
});

paper.on('cell:pointermove', function (cellView, evt, x, y) {

    var bbox = cellView.getBBox();
    var constrained = false;

    var constrainedX = x;

    if (bbox.x <= 0) { constrainedX = x + gridSize; constrained = true }
    if (bbox.x + bbox.width >= width) { constrainedX = x - gridSize; constrained = true }

    var constrainedY = y;

    if (bbox.y <= 0) {  constrainedY = y + gridSize; constrained = true }
    if (bbox.y + bbox.height >= height) { constrainedY = y - gridSize; constrained = true }

    //if you fire the event all the time you get a stack overflow
    if (constrained) { cellView.pointermove(evt, constrainedX, constrainedY) }
});
Strontian answered 22/6, 2015 at 21:35 Comment(1)
If it works for you could you unaccept myprevious answer? I think it would be better if people visiting this question in the future saw the second answer first :)Strontian
S
2

Edit: I think this approach is still feasible,but I now think my other answer is simpler/better.

The JointJS docs provide a sample where the movement of a shape is contrained to lie on an ellipse:

http://www.jointjs.com/tutorial/constraint-move-to-circle

It works by

  1. Defining a new view for your element, extending joint.dia.ElementView
  2. Overiding the pointerdown and pointermove event in the view to implement the constraint. This is done by calculating a new position, based on the mouse position and the constraint, and then passing this to the base ElementView event handler
  3. Forcing the paper to use your custom element view

This approach can be easily adapted to prevent a shape being dragged off the edge of your paper. In step 2, instead of calculating the intersection with the ellipse as in the tutorial, you would use Math.min() or Math.max() to calculate a new position.

Strontian answered 8/5, 2015 at 16:12 Comment(2)
thanks, seems like this is the way to go, although still unsure of how to calculate it but ill keep messing with it.Counteraccusation
hmm. I have an item on my backlog to do this. it was quite low down, but I'll see if I can push it up and then post the solution here ;o)Strontian

© 2022 - 2024 — McMap. All rights reserved.