flyingL123
flyingL123

Reputation: 8076

Update fabric.js Path points dynamically

I'm trying to add points to a path object dynamically. When I do, the path renders correctly, but the bounding rectangle never gets updated, making it nearly impossible for a user to select and move the path on canvas.

As you can see in the code below, the path is initially created with a single point, then I dynamically add a second point as well as a control point. After doing this, the bounding rectangle never updates:

var canvas = new fabric.Canvas('c');
canvas.backgroundColor = '#f5f5f5';
var path = new fabric.Path('M 0 20',{
    left: 100,
    top: 100,
    stroke: 'black',
    fill: ''
});

canvas.add(path);

var commandArray = [];
commandArray[0] = 'Q';
commandArray[1] = 50;
commandArray[2] = 100;
commandArray[3] = 100;
commandArray[4] = 20;

path.path[1] = commandArray;

canvas.renderAll();

I also tried calling path.setCoords(), but that did not make any difference. How can I get the bounding rectangle to update its dimensions after adding points to a path?

Here's a fiddle: http://jsfiddle.net/flyingL123/17ueLva2/2/

Upvotes: 4

Views: 12718

Answers (5)

goodbot13
goodbot13

Reputation: 1

Fabric 4.6.0

If you look at constructor of Path, it calls

fabric.Polyline.prototype._setPositionDimensions.call(this, options);

in the end. Where this is your Path and options is your Path constructor second argument. It is enough to call the method above on each path addition/removal to update the position and bounds.

Upvotes: 0

ezpzlmnsqz1337
ezpzlmnsqz1337

Reputation: 424

In fabric 3.3.2 I solved it combining the answers above:

var dims = path._calcDimensions()
path.set({
  width: dims.width,
  height: dims.height,
  left: dims.left,
  top: dims.top,
  pathOffset: {
    x: dims.width / 2 + dims.left,
    y: dims.height / 2 + dims.top
  },
  dirty: true
})
path.setCoords()

This correctly updates my path bounding box after adding points like:

path.set({path: points})

I am not sure though, if this works with negative top and left values, but I didn't need that in my case. I guess the main thing is that the _parseDimensions() method was renamed to _calcDimensions().

Upvotes: 7

kadir_ilkimen
kadir_ilkimen

Reputation: 21

I couldn't find any new way to do this. But I figured out something like below;

  1. create an SVG path string with modifications.
  2. create a new fabric path object.
  3. replace path, width, height and pathOffset properties of the original path object with the properties of the new path object.
  4. setCoords. renderAll etc...

It may not be much efficient. But it was the solution for me.

    var pathObject = new fabric.Path("M0,0 L100,100 ~ Z");

    var updatedPath =  new fabric.Path("M50,100 L120,46 ~ Z");

    pathObject.set({
      path : updatedPath.path,
      width : updatedPath.width,
      height : updatedPath.height,
      pathOffset: updatedPath.pathOffset
    });
    pathObject.setCoords();

On my setup, it says path._parseDimensions is not a function. I didn't try to solve it. I have to change all path content. So my solution seems better for me :)

Upvotes: 2

flyingL123
flyingL123

Reputation: 8076

It ended up being more complicated. If a point is added to the path that results in _parseDimensions returning a left value that is negative, the path would jump around the screen. For my use case, I need the path to stay in place while points are added and manipulated. This fiddle shows my working solution:

http://jsfiddle.net/flyingL123/8763bx2q/8/

If you run it with a JS console open, you will see the script pausing after each additional point is added, or current point is manipulated. As this happens you will see that the path does not get moved along the canvas, which is the desired behavior. After all the break points complete, you will see that the curve is centered within its selection box.

If there is an easier way to achieve this behavior, I would love to know.

Here's the function I'm using to set the dimensions just in case the fiddle link ever goes away:

function updateDims() {
    var dims = path._parseDimensions(),
        prevDims = path.prevDims || {},
        leftDiff = dims.left - (prevDims.left || 0),
        topDiff = dims.top - (prevDims.top || 0);

    path.setWidth(dims.width);
    path.setHeight(dims.height);

    if (dims.left < 0) {
        path.pathOffset.x = path.width/2 + dims.left;
        path.left = path.left + leftDiff;
    } else {
        path.pathOffset.x = path.width/2;
    }

    if (dims.top < 0) {
        path.pathOffset.y = path.height/2 + dims.top;
        path.top = path.top + topDiff;
    } else {
         path.pathOffset.y = path.height/2;   
    }

    path.prevDims = dims;
    path.setCoords();
}

Upvotes: 4

AndreaBogazzi
AndreaBogazzi

Reputation: 14731

Please, fabricjs does not support adding point dinamically as of now. To make it work you can add points like you are doing and then use internal method path._parseDimensions() each time you add points and desire to update bounding box dimension.

var dims = path._parseDimensions();
path.setWidth(dims.width);
path.setHeight(dims.height);
path.pathOffset.x = path.width/2;
path.pathOffset.y = path.height/2;
path.setCoords();

Look this updated fiddle that has the necessary code to solve your problem. I hope it works for every situation.

http://jsfiddle.net/17ueLva2/6/

Upvotes: 4

Related Questions