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
i eventually gave up and used a web view