Shopify / react-native-skia

High-performance React Native Graphics using Skia

Home Page:https://shopify.github.io/react-native-skia

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Canvas onTouch Android

chiefchief opened this issue · comments

Description

When user tap and hold, triggers - "onStart" but no "onActive" on move, immediately after start moving triggers "onEnd"

Version

0.1.240

Steps to reproduce

Code provided below

Snack, code example, screenshot, or link to a repository

import { Canvas, Path, Skia, useCanvasRef, useTouchHandler } from '@shopify/react-native-skia';
import React, { forwardRef, useEffect, useImperativeHandle } from 'react';

const activePath = Skia.Path.Make();

export const SignaturePad = forwardRef<SignaturePad, SignaturePadProps>(({ height, width, onUpdate }, ref) => {
  const canvasRef = useCanvasRef();

  useEffect(() => {
    return () => {
      activePath.reset();
    };
  }, []);

  useImperativeHandle(ref, () => ({
    clear: () => {
      activePath.reset();
      onUpdate?.('');
    },
  }));

  const onTouch = useTouchHandler({
    onStart: ({ x, y }) => {
      activePath.moveTo(x, y);
    },
    onActive: ({ x, y }) => {
      activePath.lineTo(x, y);
    },
    onEnd: () => {
      console.log('END', Date.now());
    },
  });

  return (
    <Canvas ref={canvasRef} style={{ height, width, backgroundColor: 'white' }} onTouch={onTouch}>
      <Path path={activePath} />
    </Canvas>
  );
});

type SignaturePadProps = {
  width: number;
  height: number;
  onUpdate?: (img: string) => void;
};

export type SignaturePad = {
  clear: () => void;
};

I strongly recommend to use gesture-handler for such scenario. We are not really supporting this API anymore: https://shopify.github.io/react-native-skia/docs/animations/gestures Based on the code you provided you will reanimated if want to update the path on the UI thread and have it shown directly onto the canvas. I hope this helps. William

On Fri, Feb 23, 2024 at 3:12 PM Silver O @.> wrote: Description When user tap and hold, triggers - "onStart" but no "onActive" on move, immediately after start moving triggers "onEnd" Version 0.1.240 Steps to reproduce Code provided below Snack, code example, screenshot, or link to a repository import { Canvas, Path, Skia, useCanvasRef, useTouchHandler } from @./react-native-skia'; import React, { forwardRef, useEffect, useImperativeHandle } from 'react'; const activePath = Skia.Path.Make(); export const SignaturePad = forwardRef<SignaturePad, SignaturePadProps>(({ height, width, onUpdate }, ref) => { const canvasRef = useCanvasRef(); useEffect(() => { return () => { activePath.reset(); }; }, []); useImperativeHandle(ref, () => ({ clear: () => { activePath.reset(); onUpdate?.(''); }, })); const onTouch = useTouchHandler({ onStart: ({ x, y }) => { activePath.moveTo(x, y); }, onActive: ({ x, y }) => { activePath.lineTo(x, y); }, onEnd: () => { console.log('END', Date.now()); }, }); return ( <Canvas ref={canvasRef} style={{ height, width, backgroundColor: 'white' }} onTouch={onTouch}> ); }); type SignaturePadProps = { width: number; height: number; onUpdate?: (img: string) => void; }; export type SignaturePad = { clear: () => void; }; — Reply to this email directly, view it on GitHub or unsubscribe. You are receiving this email because you are subscribed to this thread. Triage notifications on the go with GitHub Mobile for iOS or Android.

  1. on ios throw error
import { Canvas, Path, Skia, useCanvasRef } from '@shopify/react-native-skia';
import React, { forwardRef, useEffect, useImperativeHandle } from 'react';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';

const activePath = Skia.Path.Make();

const onBeginWorklet = (e) => {
  'worklet';
  activePath.moveTo(e.x, e.y);
};
const onChangeWorklet = (e) => {
  'worklet';
  activePath.lineTo(e.x, e.y);
};

export const SignaturePad = forwardRef<SignaturePad, SignaturePadProps>(({ height, width, onUpdate }, ref) => {
  const canvasRef = useCanvasRef();

  useEffect(() => {
    return () => {
      activePath.reset();
    };
  }, []);

  useImperativeHandle(ref, () => ({
    clear: () => {
      activePath.reset();
      onUpdate?.('');
    },
  }));

  const gesture = Gesture.Pan().onBegin(onBeginWorklet).onChange(onChangeWorklet);

  return (
    <GestureDetector gesture={gesture}>
      <Canvas ref={canvasRef} style={{ height, width, backgroundColor: 'white' }}>
        <Path path={activePath} style={'stroke'} strokeWidth={2} />
      </Canvas>
    </GestureDetector>
  );
});

type SignaturePadProps = {
  width: number;
  height: number;
  onUpdate?: (img: string) => void;
};

export type SignaturePad = {
  clear: () => void;
};
Screenshot 2024-02-23 at 18 30 33
  1. if run on JS the app just crash and no error
const onBeginWorklet = (e) => {
  runOnJS(activePath.moveTo)(e.x, e.y);
};
const onChangeWorklet = (e) => {
  runOnJS(activePath.lineTo)(e.x, e.y);
};
  1. on Android these actions don't trigger at all

The only way on Android that works almost correctly is this one.

import { Canvas, Path, Skia, useCanvasRef } from '@shopify/react-native-skia';
import React, { forwardRef, useEffect, useImperativeHandle } from 'react';

const activePath = Skia.Path.Make();

export const SignaturePad = forwardRef<SignaturePad, SignaturePadProps>(({ height, width, onUpdate }, ref) => {
  const canvasRef = useCanvasRef();

  useEffect(() => {
    return () => {
      activePath.reset();
    };
  }, []);

  useImperativeHandle(ref, () => ({
    clear: () => {
      activePath.reset();
      onUpdate?.('');
    },
  }));

  return (
    <Canvas
      ref={canvasRef}
      style={{ height, width, backgroundColor: 'white' }}
      onTouchStart={(e) => {
        activePath.moveTo(e.nativeEvent.locationX, e.nativeEvent.locationY);
      }}
      onTouchMove={(e) => {
        activePath.lineTo(e.nativeEvent.locationX, e.nativeEvent.locationY);
      }}
    >
      <Path path={activePath} style={'stroke'} strokeWidth={2} />
    </Canvas>
  );
});

type SignaturePadProps = {
  width: number;
  height: number;
  onUpdate?: (img: string) => void;
};

export type SignaturePad = {
  clear: () => void;
};

However, the line appears on the next tap (for example, when the user taps and moves, nothing appears, but on the next tap, the previous line will appear).

useTouchHandler works properly without modal on android