react-nativereact-native-reanimatedreact-native-svg

massive frame drops on reanimated pinch and pan gesture using reanimated on react-native-svg


I am trying to implement pinch and pan gestures to move and zoom an SVG. This SVG has multiple paths, and I am trying to implement zoom without losing any quality of the SVG, so I have to scale the SVG itself on pinch gesture. However, this causes performance issues and frame drops. Is there a way that I can optimize the code or do something to enhance the performance of the app?

i am using reanimated version 3.12.1

Here is the code of the how I implemented it:

import {
    Dimensions,
    View,
  } from 'react-native';
  import React, {} from 'react';
  import Animated, {
    useAnimatedProps,
    useSharedValue,
  } from 'react-native-reanimated';
  import {
    Gesture,
    GestureDetector,
    GestureHandlerRootView,
  } from 'react-native-gesture-handler';
  import Svg, {Path, G} from 'react-native-svg';
  const AnimatedGroup = Animated.createAnimatedComponent(G);
  
  const {width, height} = Dimensions.get('screen');
  
const PinchPan = () => {

    function clamp(val: number, min: number, max: number) {
        return Math.min(Math.max(val, min), max);
        }

    // ================= PAN =======================
    const translationX = useSharedValue(0);
    const translationY = useSharedValue(0);
    const prevTranslationX = useSharedValue(0);
    const prevTranslationY = useSharedValue(0);

    const pan = Gesture.Pan()
        .minDistance(1)
        .onStart(() => {
        prevTranslationX.value = translationX.value;
        prevTranslationY.value = translationY.value;
        })
        .onUpdate(event => {
        const maxTranslateX = width / 2;
        const maxTranslateY = height / 2;

        translationX.value = clamp(
            prevTranslationX.value + event.translationX,
            -maxTranslateX,
            maxTranslateX,
        );
        translationY.value = clamp(
            prevTranslationY.value + event.translationY,
            -maxTranslateY,
            maxTranslateY,
        );
        })
        .runOnJS(true);
    // ============================================

    // ================== pinch ===================
    const scale = useSharedValue(1);
    const startScale = useSharedValue(0);

    const pinch = Gesture.Pinch()
        .onStart(() => {
        startScale.value = scale.value;
        })
        .onUpdate(event => {
        scale.value = clamp(startScale.value * event.scale, 1, 20);
        })
        .runOnJS(true);
    // ============================================

    const composed = Gesture.Simultaneous(pan, pinch);

    const animatedPropsPinch = useAnimatedProps(() => ({
        transform: [{scale: scale.value}],
    }));
    const animatedPropsPan = useAnimatedProps(() => ({
        transform: [
        {translateX: translationX.value},
        {translateY: translationY.value},
        ],
    }));

    return (
        <View style={styles.container}>
        <GestureHandlerRootView>
            <GestureDetector gesture={composed}>
            <View>
                <Svg
                width={width}
                height={height}
                stroke-linecap="round"
                stroke-linejoin="round"
                stroke-width=".1"
                viewBox={`0 0 ${width} ${height}`}>
                {/* pinch */}
                <AnimatedGroup
                    animatedProps={animatedPropsPinch}
                    translateX={width / 2}
                    translateY={height / 2.7}>
                    {/* pan */}
                    <AnimatedGroup animatedProps={animatedPropsPan} scale={0.5}>
                    {svgPaths.map(path => (
                        <Path
                        key={Math.random()}
                        d={path.d}
                        translateX={-width / 2}
                        translateY={-height / 2}
                        fill={'grey'}
                        stroke={'black'}
                        onPress={() => 
                            console.log('path pressed')}
                        />
                    ))}
                    </AnimatedGroup>
                </AnimatedGroup>
                </Svg>
            </View>
            </GestureDetector>
        </GestureHandlerRootView>
        </View>
    );
};

the length of paths can be around or above 50


Solution

  • i eventually gave up and used a web view