admin管理员组

文章数量:1025217

What I'm trying to create is a small canvas widget that would allow a user to dynamically create a shape onto an image and then place it above an area that caught their interest, effectively it is a highlighter.

The problem is with adding a zoom function, as when I zoom onto the image I would like to ensure that;

  1. There is no possible way for the dynamically created shape to be dragged anywhere outside the image area. (pleted - ish, relies on 2nd step)
  2. You cannot drag the image out of the page view, the canvas area cannot show white space. Part of the image must always be shown, and fill the entire canvas area. (problem)

Here are two examples that I've drawn up, neither of which work correctly;

First example - getBoundingRect does not update and is bound to the image

Second example - getBoundingRect does update and is bound to the grouped object

From the link description you can see that I think I've narrowed the problem down, or at least noticed a key difference between the scripts with how the getBoundingRect behaves.

The first plunk seems to work fine, until you try to zoom in multiple times and at a greater zoom level, then it seems to start bugging out (may take a few clicks and a bit of messing around, it is very inconsistent). The second plunk is very jittery and doesn't work very well.

I've been stuck on this for a week or so now, and I'm at breaking point! So really hoping someone can point out what I'm doing wrong?

Code snippet below for first plunk;

  // creates group

  var objs = canvas.getObjects();


  var group = new fabric.Group(objs, {
    status: 'moving'
  });

  // sets grouped object position

  var originalX = active.left,
    originalY = active.top,
    mouseX = evt.e.pageX,
    mouseY = evt.e.pageY;

  active.on('moving', function(evt) {
    group.left += evt.e.pageX - mouseX;
    group.top += evt.e.pageY - mouseY;
    active.left = originalX;
    active.top = originalY;
    originalX = active.left;
    originalY = active.top;
    mouseX = evt.e.pageX;
    mouseY = evt.e.pageY;


    // sets boundary area for image when zoomed
    // THIS IS THE PART THAT DOESN'T WORK

    active.setCoords();

    // SET BOUNDING RECT TO 'active'

    var boundingRect = active.getBoundingRect();

    var zoom = canvas.getZoom();
    var viewportMatrix = canvas.viewportTransform;

    // scales bounding rect when zoomed

    boundingRect.top = (boundingRect.top - viewportMatrix[5]) / zoom;
    boundingRect.left = (boundingRect.left - viewportMatrix[4]) / zoom;
    boundingRect.width /= zoom;
    boundingRect.height /= zoom;

    var canvasHeight = canvas.height / zoom,
      canvasWidth = canvas.width / zoom,
      rTop = boundingRect.top + boundingRect.height,
      rLeft = boundingRect.left + boundingRect.width;

    // checks top left

    if (rTop < canvasHeight || rLeft < canvasWidth) {
      group.top = Math.max(group.top, canvasHeight - boundingRect.height);
      group.left = Math.max(group.left, canvasWidth - boundingRect.width);
    }

    // checks bottom right

    if (rTop > 0 || rLeft > 0) {
      group.top = Math.min(group.top, canvas.height - boundingRect.height + active.top - boundingRect.top);
      group.left = Math.min(group.left, canvas.width - boundingRect.width + active.left - boundingRect.left);
    }


  });

  // deactivates all objects on mouseup

  active.on('mouseup', function() {
    active.off('moving');
    canvas.deactivateAll().renderAll();

  })

  // sets group

  canvas.setActiveGroup(group.setCoords()).renderAll();


}

EDIT:

I've added ments and tried to simplify the code in the plunks.

The relevant code starts within the if (active.id == "img") { code block.

I've put irrelevant code as functions at the bottom, they can largely be ignored. ( createNewRect() + preventRectFromLeaving() )

I've removed one of the plunks to avoid confusion.

Let me know if it helps, or If I should try to simplify further.

Thanks!

What I'm trying to create is a small canvas widget that would allow a user to dynamically create a shape onto an image and then place it above an area that caught their interest, effectively it is a highlighter.

The problem is with adding a zoom function, as when I zoom onto the image I would like to ensure that;

  1. There is no possible way for the dynamically created shape to be dragged anywhere outside the image area. (pleted - ish, relies on 2nd step)
  2. You cannot drag the image out of the page view, the canvas area cannot show white space. Part of the image must always be shown, and fill the entire canvas area. (problem)

Here are two examples that I've drawn up, neither of which work correctly;

First example - getBoundingRect does not update and is bound to the image

Second example - getBoundingRect does update and is bound to the grouped object

From the link description you can see that I think I've narrowed the problem down, or at least noticed a key difference between the scripts with how the getBoundingRect behaves.

The first plunk seems to work fine, until you try to zoom in multiple times and at a greater zoom level, then it seems to start bugging out (may take a few clicks and a bit of messing around, it is very inconsistent). The second plunk is very jittery and doesn't work very well.

I've been stuck on this for a week or so now, and I'm at breaking point! So really hoping someone can point out what I'm doing wrong?

Code snippet below for first plunk;

  // creates group

  var objs = canvas.getObjects();


  var group = new fabric.Group(objs, {
    status: 'moving'
  });

  // sets grouped object position

  var originalX = active.left,
    originalY = active.top,
    mouseX = evt.e.pageX,
    mouseY = evt.e.pageY;

  active.on('moving', function(evt) {
    group.left += evt.e.pageX - mouseX;
    group.top += evt.e.pageY - mouseY;
    active.left = originalX;
    active.top = originalY;
    originalX = active.left;
    originalY = active.top;
    mouseX = evt.e.pageX;
    mouseY = evt.e.pageY;


    // sets boundary area for image when zoomed
    // THIS IS THE PART THAT DOESN'T WORK

    active.setCoords();

    // SET BOUNDING RECT TO 'active'

    var boundingRect = active.getBoundingRect();

    var zoom = canvas.getZoom();
    var viewportMatrix = canvas.viewportTransform;

    // scales bounding rect when zoomed

    boundingRect.top = (boundingRect.top - viewportMatrix[5]) / zoom;
    boundingRect.left = (boundingRect.left - viewportMatrix[4]) / zoom;
    boundingRect.width /= zoom;
    boundingRect.height /= zoom;

    var canvasHeight = canvas.height / zoom,
      canvasWidth = canvas.width / zoom,
      rTop = boundingRect.top + boundingRect.height,
      rLeft = boundingRect.left + boundingRect.width;

    // checks top left

    if (rTop < canvasHeight || rLeft < canvasWidth) {
      group.top = Math.max(group.top, canvasHeight - boundingRect.height);
      group.left = Math.max(group.left, canvasWidth - boundingRect.width);
    }

    // checks bottom right

    if (rTop > 0 || rLeft > 0) {
      group.top = Math.min(group.top, canvas.height - boundingRect.height + active.top - boundingRect.top);
      group.left = Math.min(group.left, canvas.width - boundingRect.width + active.left - boundingRect.left);
    }


  });

  // deactivates all objects on mouseup

  active.on('mouseup', function() {
    active.off('moving');
    canvas.deactivateAll().renderAll();

  })

  // sets group

  canvas.setActiveGroup(group.setCoords()).renderAll();


}

EDIT:

I've added ments and tried to simplify the code in the plunks.

The relevant code starts within the if (active.id == "img") { code block.

I've put irrelevant code as functions at the bottom, they can largely be ignored. ( createNewRect() + preventRectFromLeaving() )

I've removed one of the plunks to avoid confusion.

Let me know if it helps, or If I should try to simplify further.

Thanks!

Share Improve this question edited Oct 27, 2016 at 13:30 alexc asked Oct 25, 2016 at 13:09 alexcalexc 1,3202 gold badges18 silver badges46 bronze badges 2
  • this is a ton of code in each example. any way to boil it down in to simpler examples with less code? Very hard to tell what is happening in each. – StefanHayden Commented Oct 26, 2016 at 0:28
  • 2 @StefanHayden Done! There isn't less code, but I've moved things around and added ments to try make it easier, let me know if it makes it easier to read? (also added edit section to question) Thanks! – alexc Commented Oct 26, 2016 at 7:52
Add a ment  | 

1 Answer 1

Reset to default 6 +100

I think that the grouping was messing with the position of the background image. So, I tried removing the group when the image is moving and manually updating the position of the rect instead.

It sets the last position of the image before moving

var lastLeft = active.left,
    lastTop = active.top;

And then it updates those and the position of the rect every time the image moves

rect.left += active.left - lastLeft;
rect.top += active.top - lastTop;
// I think this is needed so the rectangle can be re-selected
rect.setCoords();
lastLeft = active.left;
lastTop = active.top;

Since the image has to stay within the canvas, the rect stays inside the canvas, too, whenever the image moves. The rest of the code you wrote seemed to work fine.

http://plnkr.co/edit/6GGcUxGC7CjcyQzExMoK?p=preview

What I'm trying to create is a small canvas widget that would allow a user to dynamically create a shape onto an image and then place it above an area that caught their interest, effectively it is a highlighter.

The problem is with adding a zoom function, as when I zoom onto the image I would like to ensure that;

  1. There is no possible way for the dynamically created shape to be dragged anywhere outside the image area. (pleted - ish, relies on 2nd step)
  2. You cannot drag the image out of the page view, the canvas area cannot show white space. Part of the image must always be shown, and fill the entire canvas area. (problem)

Here are two examples that I've drawn up, neither of which work correctly;

First example - getBoundingRect does not update and is bound to the image

Second example - getBoundingRect does update and is bound to the grouped object

From the link description you can see that I think I've narrowed the problem down, or at least noticed a key difference between the scripts with how the getBoundingRect behaves.

The first plunk seems to work fine, until you try to zoom in multiple times and at a greater zoom level, then it seems to start bugging out (may take a few clicks and a bit of messing around, it is very inconsistent). The second plunk is very jittery and doesn't work very well.

I've been stuck on this for a week or so now, and I'm at breaking point! So really hoping someone can point out what I'm doing wrong?

Code snippet below for first plunk;

  // creates group

  var objs = canvas.getObjects();


  var group = new fabric.Group(objs, {
    status: 'moving'
  });

  // sets grouped object position

  var originalX = active.left,
    originalY = active.top,
    mouseX = evt.e.pageX,
    mouseY = evt.e.pageY;

  active.on('moving', function(evt) {
    group.left += evt.e.pageX - mouseX;
    group.top += evt.e.pageY - mouseY;
    active.left = originalX;
    active.top = originalY;
    originalX = active.left;
    originalY = active.top;
    mouseX = evt.e.pageX;
    mouseY = evt.e.pageY;


    // sets boundary area for image when zoomed
    // THIS IS THE PART THAT DOESN'T WORK

    active.setCoords();

    // SET BOUNDING RECT TO 'active'

    var boundingRect = active.getBoundingRect();

    var zoom = canvas.getZoom();
    var viewportMatrix = canvas.viewportTransform;

    // scales bounding rect when zoomed

    boundingRect.top = (boundingRect.top - viewportMatrix[5]) / zoom;
    boundingRect.left = (boundingRect.left - viewportMatrix[4]) / zoom;
    boundingRect.width /= zoom;
    boundingRect.height /= zoom;

    var canvasHeight = canvas.height / zoom,
      canvasWidth = canvas.width / zoom,
      rTop = boundingRect.top + boundingRect.height,
      rLeft = boundingRect.left + boundingRect.width;

    // checks top left

    if (rTop < canvasHeight || rLeft < canvasWidth) {
      group.top = Math.max(group.top, canvasHeight - boundingRect.height);
      group.left = Math.max(group.left, canvasWidth - boundingRect.width);
    }

    // checks bottom right

    if (rTop > 0 || rLeft > 0) {
      group.top = Math.min(group.top, canvas.height - boundingRect.height + active.top - boundingRect.top);
      group.left = Math.min(group.left, canvas.width - boundingRect.width + active.left - boundingRect.left);
    }


  });

  // deactivates all objects on mouseup

  active.on('mouseup', function() {
    active.off('moving');
    canvas.deactivateAll().renderAll();

  })

  // sets group

  canvas.setActiveGroup(group.setCoords()).renderAll();


}

EDIT:

I've added ments and tried to simplify the code in the plunks.

The relevant code starts within the if (active.id == "img") { code block.

I've put irrelevant code as functions at the bottom, they can largely be ignored. ( createNewRect() + preventRectFromLeaving() )

I've removed one of the plunks to avoid confusion.

Let me know if it helps, or If I should try to simplify further.

Thanks!

What I'm trying to create is a small canvas widget that would allow a user to dynamically create a shape onto an image and then place it above an area that caught their interest, effectively it is a highlighter.

The problem is with adding a zoom function, as when I zoom onto the image I would like to ensure that;

  1. There is no possible way for the dynamically created shape to be dragged anywhere outside the image area. (pleted - ish, relies on 2nd step)
  2. You cannot drag the image out of the page view, the canvas area cannot show white space. Part of the image must always be shown, and fill the entire canvas area. (problem)

Here are two examples that I've drawn up, neither of which work correctly;

First example - getBoundingRect does not update and is bound to the image

Second example - getBoundingRect does update and is bound to the grouped object

From the link description you can see that I think I've narrowed the problem down, or at least noticed a key difference between the scripts with how the getBoundingRect behaves.

The first plunk seems to work fine, until you try to zoom in multiple times and at a greater zoom level, then it seems to start bugging out (may take a few clicks and a bit of messing around, it is very inconsistent). The second plunk is very jittery and doesn't work very well.

I've been stuck on this for a week or so now, and I'm at breaking point! So really hoping someone can point out what I'm doing wrong?

Code snippet below for first plunk;

  // creates group

  var objs = canvas.getObjects();


  var group = new fabric.Group(objs, {
    status: 'moving'
  });

  // sets grouped object position

  var originalX = active.left,
    originalY = active.top,
    mouseX = evt.e.pageX,
    mouseY = evt.e.pageY;

  active.on('moving', function(evt) {
    group.left += evt.e.pageX - mouseX;
    group.top += evt.e.pageY - mouseY;
    active.left = originalX;
    active.top = originalY;
    originalX = active.left;
    originalY = active.top;
    mouseX = evt.e.pageX;
    mouseY = evt.e.pageY;


    // sets boundary area for image when zoomed
    // THIS IS THE PART THAT DOESN'T WORK

    active.setCoords();

    // SET BOUNDING RECT TO 'active'

    var boundingRect = active.getBoundingRect();

    var zoom = canvas.getZoom();
    var viewportMatrix = canvas.viewportTransform;

    // scales bounding rect when zoomed

    boundingRect.top = (boundingRect.top - viewportMatrix[5]) / zoom;
    boundingRect.left = (boundingRect.left - viewportMatrix[4]) / zoom;
    boundingRect.width /= zoom;
    boundingRect.height /= zoom;

    var canvasHeight = canvas.height / zoom,
      canvasWidth = canvas.width / zoom,
      rTop = boundingRect.top + boundingRect.height,
      rLeft = boundingRect.left + boundingRect.width;

    // checks top left

    if (rTop < canvasHeight || rLeft < canvasWidth) {
      group.top = Math.max(group.top, canvasHeight - boundingRect.height);
      group.left = Math.max(group.left, canvasWidth - boundingRect.width);
    }

    // checks bottom right

    if (rTop > 0 || rLeft > 0) {
      group.top = Math.min(group.top, canvas.height - boundingRect.height + active.top - boundingRect.top);
      group.left = Math.min(group.left, canvas.width - boundingRect.width + active.left - boundingRect.left);
    }


  });

  // deactivates all objects on mouseup

  active.on('mouseup', function() {
    active.off('moving');
    canvas.deactivateAll().renderAll();

  })

  // sets group

  canvas.setActiveGroup(group.setCoords()).renderAll();


}

EDIT:

I've added ments and tried to simplify the code in the plunks.

The relevant code starts within the if (active.id == "img") { code block.

I've put irrelevant code as functions at the bottom, they can largely be ignored. ( createNewRect() + preventRectFromLeaving() )

I've removed one of the plunks to avoid confusion.

Let me know if it helps, or If I should try to simplify further.

Thanks!

Share Improve this question edited Oct 27, 2016 at 13:30 alexc asked Oct 25, 2016 at 13:09 alexcalexc 1,3202 gold badges18 silver badges46 bronze badges 2
  • this is a ton of code in each example. any way to boil it down in to simpler examples with less code? Very hard to tell what is happening in each. – StefanHayden Commented Oct 26, 2016 at 0:28
  • 2 @StefanHayden Done! There isn't less code, but I've moved things around and added ments to try make it easier, let me know if it makes it easier to read? (also added edit section to question) Thanks! – alexc Commented Oct 26, 2016 at 7:52
Add a ment  | 

1 Answer 1

Reset to default 6 +100

I think that the grouping was messing with the position of the background image. So, I tried removing the group when the image is moving and manually updating the position of the rect instead.

It sets the last position of the image before moving

var lastLeft = active.left,
    lastTop = active.top;

And then it updates those and the position of the rect every time the image moves

rect.left += active.left - lastLeft;
rect.top += active.top - lastTop;
// I think this is needed so the rectangle can be re-selected
rect.setCoords();
lastLeft = active.left;
lastTop = active.top;

Since the image has to stay within the canvas, the rect stays inside the canvas, too, whenever the image moves. The rest of the code you wrote seemed to work fine.

http://plnkr.co/edit/6GGcUxGC7CjcyQzExMoK?p=preview

本文标签: