Tracer69
Tracer69

Reputation: 1110

Touch move is really slow

I'm building a paint-like feature where the user can draw a line, but the touchmove event gets emitted really slow on my device (android phone), so the line becomes edgy. As soon as I connect the device to my PC and open the chrome devtools via USB debugging, everything works fine. On the phone emulator in desktop-chrome aren't any problems.

Here is a screenshot. The inner circle was drawn with the slow touch events, and for the outer one I connected the device to my PC.

Screenshot

Here is another screenshot showing the durations between individual "touchmove" event-calls. The top part (green values) occured when the devtools were open, the bottom part (red values) when they were closed.

Screenshot

The code:

function DrawingCanvas(/* ... */) {
    // ...

    const handleTouchMove = (event) => {
        handleMouseMove(event.touches[0])
    }

    const handleMouseMove = ({ clientX, clientY }) => {
        if (!isDrawing) {
            return
        }

        const canvasRect = canvas.getBoundingClientRect()
        const x = clientX - canvasRect.x
        const y = clientY - canvasRect.y

        currentPath.current.addPoint([x, y])
        update()
    }

    const update = () => {
        clearCanvas()
        drawPath()
    }

    // ...

    useEffect(() => {
        const drawingCanvas = drawingCanvasRef.current

        // ...

        drawingCanvas.addEventListener("touchstart", handleDrawStart)
        drawingCanvas.addEventListener("touchend", handleDrawEnd)
        drawingCanvas.addEventListener("touchcancel", handleDrawEnd)
        drawingCanvas.addEventListener("touchmove", handleTouchMove)
        
        drawingCanvas.addEventListener("mousedown", handleDrawStart)
        drawingCanvas.addEventListener("mouseup", handleDrawEnd)
        drawingCanvas.addEventListener("mousemove", handleMouseMove)
        
        return () => {
            drawingCanvas.removeEventListener("touchstart", handleDrawStart)
            drawingCanvas.removeEventListener("touchmove", handleTouchMove)
            drawingCanvas.removeEventListener("touchend", handleDrawEnd)
            drawingCanvas.removeEventListener("touchcancel", handleDrawEnd)

            drawingCanvas.removeEventListener("mousedown", handleDrawStart)
            drawingCanvas.removeEventListener("mouseup", handleDrawEnd)
            drawingCanvas.removeEventListener("mousemove", handleMouseMove)
        }
    })

    return <canvas /* ... */ />
}

Does anyone have an idea on how to fix this?
You can test it by yourself on the website: https://www.easymeme69.com/editor

Upvotes: 4

Views: 2917

Answers (4)

Lily
Lily

Reputation: 11

I've had the same problem. I had no freezes on mobile or firefox, only on Chromium. Either disabling touchpad-overscroll-history-navigation in chrome-flags or e.preventDefault() can solve the problem.

Upvotes: 1

Bil5
Bil5

Reputation: 502

I'm facing exactly the same situation, I'm developing a React app with some touch features implementing actions on touchmove.

All my tests are done inside Chrome on the Debian-based Raspberry OS distro.

It results in a deadly laggy UI with a real touch screen...except (this is when it becomes very interesting!) if the console is opened with Chrome mobile emulator, then even if I try to play with my finger on the real touch screen at this moment.

touch-action: none & event.stopPropagation hacks were already existing in my code and didn't change the game.

2 conclusions on that :

  • The touch screen (and its driver) is fine
  • The CPU is quite able to handle the load

As for now, the mystery is still opaque for me.

My feeling is that, somehow, Chrome is deliberately decreasing/increasing the touch events rate depending (correspondingly) on whether we're in a real use case or whether we're on the emulator. I created a simple fiddle to validate this hypothesis: https://jsfiddle.net/ncgtjesh/20/show

It seems to be the case since I can clearly that the emulator-enabled mode outputs 240 events/second while the real non-emulated interface is stuck to 120.

I'm quite surprised that the fixes enacted in the responses above made it since it seems to be a browser implementation choice.

Upvotes: 3

Luis Fonsi VEVO
Luis Fonsi VEVO

Reputation: 83

I had this exact same thing happen to me, down to not being able to reproduce with USB debugging open. Besides the e.preventDefault() hack, you can also set the touchable element's touch-action: none; in CSS.

Upvotes: 3

Tracer69
Tracer69

Reputation: 1110

Somehow calling event.preventDefault() on the touchmove event fixed it.

Upvotes: 7

Related Questions