FabricJS Touch Pan/Zoom Entire Canvas

此生再无相见时 提交于 2019-12-07 15:10:12

问题


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.


回答1:


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.




回答2:


I had some trouble getting the panning part of Laurens code to work on Fabric 2.3.3. So in case anyone encounters the same problem I wanted to share my solution.

My problem was, that I could not get panning to work on my smartphone, even though it worked on a pc. After some debugging I found that I can solve it by replacing e.e.layerX with e.self.x (and e.e.layerY with e.self.y).

Heres the full modified code:

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 = canvas.getZoom();
            }
            var delta = zoomStartScale * e.self.scale;
            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.self.x && undefined != e.self.x) {
            currentX = e.self.x;
            currentY = e.self.y;
            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.self.x;
            lastY = e.self.y;
        }
    }
});}



回答3:


I was able to achieve multi-touch panning and zooming with the mouse:wheel event.

When doing pinch to zoom - most browsers usually emit the ctrl button as well to distinguish between a scroll operation and not.

I then utilize the DeltaX and DeltaY properties of the Fabric event to either zoom or pan similar to the sample code they provided.

Every place that I call setState below should be a variable outside the function if you are not using with React.

    this.canvas.on("mouse:wheel", opt => {
      if (!this.canvas.viewportTransform) {
        return;
      }

      var evt: any = opt.e;
      if (evt.ctrlKey === true) {
        var evt: any = opt.e;
        var deltaY = evt.deltaY;
        var zoom = this.canvas.getZoom();
        zoom = zoom - deltaY / 100;
        if (zoom > 20) zoom = 20;
        if (zoom < 0.01) zoom = 0.01;
        this.canvas.zoomToPoint(
          new fabric.Point(evt.offsetX, evt.offsetY),
          zoom
        );
      } else {
        console.log("Scrolling!!");

        //inverted scrolling enabled. Remove the -1 multiplier to disable

        this.canvas.viewportTransform[4] += (evt.deltaX)*-1; 
        this.canvas.viewportTransform[5] += (evt.deltaY)*-1;
        this.canvas.requestRenderAll();
        this.setState({ lastPosX: evt.clientX });
        this.setState({ lastPosY: evt.clientY });
      }
      opt.e.preventDefault();
      opt.e.stopPropagation();
    });


来源:https://stackoverflow.com/questions/45110576/fabricjs-touch-pan-zoom-entire-canvas

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!