fabricjs / fabric.js

Javascript Canvas Library, SVG-to-Canvas (& canvas-to-SVG) Parser
http://fabricjs.com
Other
28.66k stars 3.48k forks source link

[Bug]: Touch events are not being captured on the mobile device. #8849

Closed sejalaroraa1234 closed 2 weeks ago

sejalaroraa1234 commented 1 year ago

CheckList

Version

5.3.0

In What environments are you experiencing the problem?

Reactnative Mobile (android and IOS)

Node Version (if applicable)

19.6.0

Link To Reproduction

Using it in react native mobile app and it's private so we can't share the link.

Steps To Reproduce

Hey, I integrated fabric into my project and trying to capture the touch events of canvas but I'm not able to capture any touch event. I'm using fabric 5.3.0. I integrated it in react native by injected JS and displaying it by using react-native-webview. Below is my code

const canvas = new fabric.Canvas('canvas', {
selection:false,
height:1200,
width:1200,
backgroundColor: 'red',
zoomEnabled:true,
isDrawingMode:false,
isTouchSupported:true,
gestures: ['drag', 'zoom', 'rotate'],
preserveObjectStacking: true,
controlsAboveOverlay: true,
allowTouchScrolling: true,

});

canvas.loadFromJSON(${json}, canvas.renderAll.bind(canvas), function(o, object) {
  object.lockMovementX = true;
  object.lockMovementY = true;
  object.set('selectable', false);
  canvas.setViewportTransform([${minScale}, 0, 0, ${minScale}, ${translateX}, ${translateY}]);
  canvas.renderAll();
  })

  canvas.on('touch:gesture', function(e){
    ${console.log('touch:gesture event captured')}
    window.ReactNativeWebView.postMessage(JSON.stringify(e));
  });

  canvas.on('touchstart', function(e) {
    ${console.log('touchstart event captured')}
  });

  fabric.util.addListener(canvas.lowerCanvasEl, 'touchstart', function(e) {
    ${console.log('touchstart event captured')}
  });

In the above code, I'm using listener to capture the events but logs are not coming whenever I zoom or touch the canvas object on the mobile screen.

My canvas tag is kept like below in the html of webview:

<body><canvas id="canvas" touch-action="none"></canvas></body>

Cdn used: https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.0/fabric.min.js

Expected Behavior

The touch events should be captured whenever I touch the canvas or pinch zoom on it.

Actual Behavior

The touch events are not being captured. However, when I switch the selection property of the canvas to be true, the selected event of the canvas object is being listened!

Error Message & Stack Trace

No response

timfrio commented 1 year ago

similar situation, touch does not work on the phone

BlackCat1397 commented 1 year ago

+1 canvas.on('mouse:down', function(opt) {...}); and other mouse related events work, but I want to use pinch gesture for zoom and canvas.on('touch:gesture', ... event does not work:|

Yubayer commented 1 year ago

same probelm, touch event dosenot work

dylanBcp commented 1 year ago

+1 Same problem

touxing commented 11 months ago

+1 Same problem

asturur commented 11 months ago

did you try to use the pointer events instead of touch?

enablePointerEvents: true

when initializing canvas.

the touch start event should fire whit enablePointerEvents: false only, while the pointer events will change the mouse events into pointerdown, pointerup and so on.

harshpatel147 commented 10 months ago

hey @asturur

import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild, ViewContainerRef } from '@angular/core';
import { fabric } from 'fabric';

@Component({
  selector: 'app-canvas-draw',
  templateUrl: './canvas-draw.component.html',
  styleUrls: ['./canvas-draw.component.scss']
})
export class CanvasDrawComponent implements OnInit, AfterViewInit {
  @ViewChild('mainHtmlCanvas') mainHtmlCanvas!: ElementRef;

  mainCanvas!: fabric.Canvas;
  isDragging: boolean = false;
  selection: boolean = true;
  lastPosX!: number;
  lastPosY!: number;
  isDrawingMode: boolean = false;

  ngAfterViewInit() {
    this.mainCanvas = new fabric.Canvas(this.mainHtmlCanvas.nativeElement, {
      hoverCursor: 'pointer',
      selection: this.selection,
      selectionBorderColor: 'blue',
      width: 500,
      height: 500,
      allowTouchScrolling: true,
      isDrawingMode: this.isDrawingMode,
    });

    this.mainCanvas.on('touch:gesture', (e) => {
      console.log('touch:gesture', e);
    });

    this.mainCanvas.on('touch:drag', (e) => {
      console.log('touch:drag', e);
    });

    this.mainCanvas.on('mouse:wheel', (opt) => {
      var delta = opt.e.deltaY;
      var zoom = this.mainCanvas.getZoom();
      zoom *= 0.999 ** delta;
      if (zoom > 20) zoom = 20;
      if (zoom < 0.01) zoom = 0.01;
      this.mainCanvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
      opt.e.preventDefault();
      opt.e.stopPropagation();
      var vpt = this.mainCanvas.viewportTransform ?? [];
      if (zoom < 400 / 1000) {
        vpt[4] = 200 - 1000 * zoom / 2;
        vpt[5] = 200 - 1000 * zoom / 2;
      } else {
        if (vpt[4] >= 0) {
          vpt[4] = 0;
        } else if (vpt[4] < this.mainCanvas.getWidth() - 1000 * zoom) {
          vpt[4] = this.mainCanvas.getWidth() - 1000 * zoom;
        }
        if (vpt[5] >= 0) {
          vpt[5] = 0;
        } else if (vpt[5] < this.mainCanvas.getHeight() - 1000 * zoom) {
          vpt[5] = this.mainCanvas.getHeight() - 1000 * zoom;
        }
      }
    });

    this.mainCanvas.on('mouse:down', (opt) => {
      var evt = opt.e;
      if (evt.altKey === true) {
        this.isDragging = true;
        this.selection = false;
        this.lastPosX = evt.clientX;
        this.lastPosY = evt.clientY;
      }
    });
    this.mainCanvas.on('mouse:move', (opt) => {
      if (this.isDragging) {
        var e = opt.e;
        var vpt = this.mainCanvas.viewportTransform ?? [];
        vpt[4] += e.clientX - this.lastPosX;
        vpt[5] += e.clientY - this.lastPosY;
        this.mainCanvas.requestRenderAll();
        this.lastPosX = e.clientX;
        this.lastPosY = e.clientY;
      }
    });
    this.mainCanvas.on('mouse:up', (opt) => {
      // on mouse up we want to recalculate new interaction
      // for all objects, so we call setViewportTransform
      this.mainCanvas.setViewportTransform(this.mainCanvas.viewportTransform ?? []);
      this.isDragging = false;
      this.selection = true;
    });
  }
}

I have to add support zoom and pan for touch devices. but touch:gesture, touch:drag touch related events not working for touch devices.

I'm using fabricJs v5.3.0 in Angular (Typescript).

I read your comment about enablePointerEvents: true during initilization . when I add this option. I'm getting error

Error: src/main/webapp/app/admin/fabricjs-canvas-draw/canvas-draw/canvas-draw.component.ts:158:7 - error TS2345: Argument of type '{ hoverCursor: string; selection: boolean; selectionBorderColor: string; width: number | undefined; height: number | undefined; allowTouchScrolling: true; isDrawingMode: boolean; enablePointerEvents: boolean; }' is not assignable to parameter of type 'ICanvasOptions'.
  Object literal may only specify known properties, and 'enablePointerEvents' does not exist in type 'ICanvasOptions'.

158       enablePointerEvents: true
          ~~~~~~~~~~~~~~~~~~~~~~~~~

✖ Failed to compile.
    "fabric": "^5.3.0",
    "@types/fabric": "^5.3.4",
    "typescript": "4.8.2",
    "angular": "14.2.0"

You can test it on stackblitz

Jogii commented 8 months ago

Touch events does not fire with fabricjs v5.3.0. Guys, Do we have any solution or workaround for this issue? @sejalaroraa1234 @harshpatel147 @BlackCat1397 @touxing @dylanBcp @timfrio @Yubayer

timfrio commented 8 months ago

@Jogii, I just added a layer of divs on top of the fabric and transferred all touch action events with code

harshpatel147 commented 8 months ago

@Jogii, I just added a layer of divs on top of the fabric and transferred all touch action events with code

@timfrio I have to add support zoom and pan for touch devices. it is possible? can you share a working sample example?

Please check this Demo on stackblitz Zoom Works on Mouse Scroll, and Pan Works With Hold 'Alt' + Mouse Drag (Hold Down the mouse click and move mouse). but Zoom and pan not working for touch devices.

Please help me.

Jogii commented 8 months ago

@asturur @kangax @keyurpatel @melchiar @nimworks @ShaMan123 @stefanhayden @vinch
Guys, Is there any plan to fix the touch events bug? Kindly can anyone have a look at it with priority.

ShaMan123 commented 8 months ago

My plans for fabric are big and wide and can be seen with the list of ~80 open PRs I can surely fix this issue or in fact suggest something a lot more robust but it won't necessarily enter fabric for some time. @asturur what is the priority for this?

ShaMan123 commented 8 months ago

9551 related

niawjunior commented 8 months ago

I use this one

hammerjs

image

https://github.com/fabricjs/fabric.js/assets/17493195/d4f125d2-5d80-4a20-8a18-caa498dd4d91

kartiksolanki commented 8 months ago

@niawjunior : Can you please show me the code inside hammerCanvas.on('pan') function ? I am able to use hammer js with fabricjs successfully but my panning is not as smooth as it is show in your video. I would greatly appreciate any help on how did you optimized it to work smoothly?

touxing commented 7 months ago

I also use hammerjs to solution it.

image

niawjunior commented 7 months ago

@niawjunior : Can you please show me the code inside hammerCanvas.on('pan') function ? I am able to use hammer js with fabricjs successfully but my panning is not as smooth as it is show in your video. I would greatly appreciate any help on how did you optimized it to work smoothly?

Hi here for you

image image
asnyder13 commented 7 months ago

I also previously had this issue. From what I remember, the package from npm was not built with gestures, so in my package.json I have

  "scripts": {
    ...,
    "postinstall": "cd node_modules/fabric && npm install && npm run build_with_gestures"
  },
Smrtnyk commented 7 months ago

I also previously had this issue. From what I remember, the package from npm was not built with gestures, so in my package.json I have

  "scripts": {
    ...,
    "postinstall": "cd node_modules/fabric && npm install && npm run build_with_gestures"
  },

thats weird, I was expecting fabric to only ship with dist files and not source code

asturur commented 7 months ago

@Smrtnyk Fabric ships with both code and dist file because of previous building logic. It still ships with everything because of it, and it may continue to do so, i m not sure what can be done with tools, but i don't that with the latest package.json the src are reacheable during import, but they should be with scripts during the install phase.

Is that an issue for some procedure?

Regarding touch:events in general, touch events should work and touch events are:

touchend
touchmove
touchstart

and touchcancel that we don't use.

if you prevent pointer events you can enable those too.

If you are asking for gesture implementations, i don't think those should be part of the core fabric functionality.

Is there even a still maintained good gesture library to make an example with? Because we could have a guide for integration yes, but last time i searched i didn't find a library that is used more than the others or that look better. Last time i used Hammer.js and implementing gestures was quite easy

ShaMan123 commented 7 months ago

What gestures do we need? I think we can add s scale + rotate gesture and the rest is trivial isn't it?

krmanik commented 6 months ago

I have implemented like this, may be helpful.


import Hammer from "hammerjs";

const minScale = 0.1;
const maxScale = 10;
let zoomScale = 1;
let currentScale = 1;

export const enableZoom = (canvas) => {
    canvas.on("mouse:move", onMouseMove);

    const hammer = new Hammer(canvas.upperCanvasEl);
    hammer.get("pinch").set({ enable: true });
    hammer.on("pinch pinchmove", ev => {
        currentScale = Math.min(Math.max(minScale, ev.scale * zoomScale), maxScale);
    });
    hammer.on("pinchend pinchcancel", () => {
        zoomScale = currentScale;
    });
};

export const disableZoom = (canvas) => {
    canvas.off("mouse:move", onMouseMove);

    const hammer = new Hammer(canvas.upperCanvasEl);
    hammer.get("pinch").set({ enable: false });
    hammer.off("pinch pinchmove pinchend pinchcancel");
};

const onMouseMove = (opt) => {
     if ((e.type === "touchmove") && (e.touches.length > 1)) {
        canvas.zoomToPoint({ x: canvas.width / 2, y: canvas.height / 2 }, currentScale);
        return;
    }
}
AdemIssadWytopia commented 3 months ago

Guys, please, where did you put those HammerJs scripts ? Can't find my way to make it work.

ralphcode commented 1 month ago

I also previously had this issue. From what I remember, the package from npm was not built with gestures, so in my package.json I have

  "scripts": {
    ...,
    "postinstall": "cd node_modules/fabric && npm install && npm run build_with_gestures"
  },

@asnyder13 Thanks. This solves the issue for me with 5.3.0.

asturur commented 2 weeks ago

There is a bit of confusion here about touch events. drag/pinch/pan are not events, are names we give to multi touch/pointer combinations of down/move/up. FabricJS core library does not take care of interpreting those as gestures, for which you can use something like Hammer.js indeed