javascriptcanvasfabricjspanning

How to implement canvas panning with Fabric.js


I have a Fabric.js canvas and I want to implement the full-canvas panning that software packages usually do with a "hand" tool. It's when you press one of the mouse buttons, then move over the canvas while holding the mouse button and the visible portion of the canvas changes accordingly.

You can see in this video what I want to achieve.

In order to implement this functionality I wrote the following code:

$(canvas.wrapperEl).on('mousemove', function(evt) {
    if (evt.button == 2) { // 2 is the right mouse button
        canvas.absolutePan({
            x: evt.clientX,
            y: evt.clientY
        });
    }
});

But it doesn't work. You can see in this video what happens.

How can I modify my code in order:

  1. For panning to work like in the first video?

  2. For the event handler to consume the event? It should prevent the context menu from appearing when the user presses or releases the right mouse button.


Solution

  • An easy way to pan a Fabric canvas in response to mouse movement is to calculate the cursor displacement between mouse events and pass it to relativePan.

    Observe how we can use the screenX and screenY properties of the previous mouse event to calculate the relative position of the current mouse event:

    function startPan(event) {
      if (event.button != 2) {
        return;
      }
      var x0 = event.screenX,
          y0 = event.screenY;
      function continuePan(event) {
        var x = event.screenX,
            y = event.screenY;
        fc.relativePan({ x: x - x0, y: y - y0 });
        x0 = x;
        y0 = y;
      }
      function stopPan(event) {
        $(window).off('mousemove', continuePan);
        $(window).off('mouseup', stopPan);
      };
      $(window).mousemove(continuePan);
      $(window).mouseup(stopPan);
      $(window).contextmenu(cancelMenu);
    };
    function cancelMenu() {
      $(window).off('contextmenu', cancelMenu);
      return false;
    }
    $(canvasWrapper).mousedown(startPan);
    

    We start panning on mousedown and continue panning on mousemove. On mouseup, we cancel the panning; we also cancel the mouseup-cancelling function itself.

    The right-click menu, also known as the context menu, is cancelled by returning false. The menu-cancelling function also cancels itself. Thus, the context menu will work if you subsequently click outside the canvas wrapper.

    Here is a page demonstrating this approach:

    http://michaellaszlo.com/so/fabric-pan/

    You will see three images on a Fabric canvas (it may take a moment or two for the images to load). You'll be able to use the standard Fabric functionality. You can left-click on the images to move them around, stretch them, and rotate them. But when you right-click within the canvas container, you pan the whole Fabric canvas with the mouse.