Search code examples
canvasfabricjs

FabricJS Touch Pan/Zoom Entire Canvas


I need to enable touch zoom/panning on a FabricJS canvas. There are libraries that will allow this behavior on an image (see pinch-zoom-canvas) or via mouse-click events (see this Fiddle) but I can't seem to get the 'touch:gesture' events hooked up properly.

I've built the library with gestures enabled (so this FabricJS demo works locally for me), but I don't know where to start with combining the gestures with the working fiddle.

I tried variations of code like this:

    canvas.on({
        'touch:gesture': function() {
            var text = document.createTextNode(' Gesture ');
            info.insertBefore(text, info.firstChild);
            // Handle zoom only if 2 fingers are touching the screen
            if (event.e.touches && event.e.touches.length == 2) {
                // Get event point
                var point = new fabric.Point(event.self.x, event.self.y);
                // Remember canvas scale at gesture start
                if (event.self.state == "start") {
                    zoomStartScale = self.canvas.getZoom();
                }
                // Calculate delta from start scale
                var delta = zoomStartScale * event.self.scale;
                // Zoom to pinch point
                self.canvas.zoomToPoint(point, delta);
            }

        },
        'touch:drag': function(e) {
            panning = true;
            var text = document.createTextNode(' Dragging ');
            info.insertBefore(text, info.firstChild);
            if (panning && e && e.e) {
                debugger;
                var units = 10;
                var delta = new fabric.Point(e.e.movementX, e.e.movementY);
                canvas.relativePan(delta);
            }
            panning = false;
        },
        'touch:longpress': function() {
            var text = document.createTextNode(' Longpress ');
            info.insertBefore(text, info.firstChild);
        }
    });

But nothing happens when I test on iPhone/iPad.


Solution

  • The pinch to zoom was a stupid mistake, I hadn't included the event in the function parameter. The code below is working for pinch/zoom and tap/drag.

        canvas.on({
            'touch:gesture': function(e) {
                if (e.e.touches && e.e.touches.length == 2) {
                    pausePanning = true;
                    var point = new fabric.Point(e.self.x, e.self.y);
                    if (e.self.state == "start") {
                        zoomStartScale = self.canvas.getZoom();
                    }
                    var delta = zoomStartScale * e.self.scale;
                    self.canvas.zoomToPoint(point, delta);
                    pausePanning = false;
                }
            },
            'object:selected': function() {
                pausePanning = true;
            },
            'selection:cleared': function() {
                pausePanning = false;
            },
            'touch:drag': function(e) {
                if (pausePanning == false && undefined != e.e.layerX && undefined != e.e.layerY) {
                    currentX = e.e.layerX;
                    currentY = e.e.layerY;
                    xChange = currentX - lastX;
                    yChange = currentY - lastY;
    
                    if( (Math.abs(currentX - lastX) <= 50) && (Math.abs(currentY - lastY) <= 50)) {
                        var delta = new fabric.Point(xChange, yChange);
                        canvas.relativePan(delta);
                    }
    
                    lastX = e.e.layerX;
                    lastY = e.e.layerY;
                }
            }
        });
    

    The absolute ~50px if/then statement is to avoid drags far away from the last point causing the canvas to jump. Also paused the panning to be able to move object independently. Pinch/zoom code was found in a github issues thread.