React Native / Gesture Handler

Gesture Handler

Өмнөх хичээлд Reanimated-г сурлаа. Одоо хуруугаараа дарах, чирэх, шударгах, нугалах зэрэг gesture-үүдийг хэрхэн таних талаар сурна. react-native-gesture-handler нь Reanimated-тай хамт ажиллахад зориулагдсан бөгөөд хоёуланг нь хослуулахад апп маш мэргэжлийн болдог.

Суулгах ба тохируулга

bash
npx expo install react-native-gesture-handler react-native-reanimated

App.tsx-н хамгийн дээд талд GestureHandlerRootView-г оруулах шаардлагатай — энэ алхмыг орхивол gesture ажиллахгүй:

typescript
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { StyleSheet } from 'react-native';

export default function App() {
  return (
    <GestureHandlerRootView style={styles.root}>
      {/* Бусад component-үүд энд */}
    </GestureHandlerRootView>
  );
}

const styles = StyleSheet.create({
  root: { flex: 1 },
});

Tap ба Long Press gesture

Gesture.Tap() нь энгийн дарах үйлдлийг илрүүлдэг. Reanimated-тай хослуулж animation нэмнэ:

typescript
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';
import { StyleSheet, Text } from 'react-native';

export default function TapExample() {
  const scale = useSharedValue(1);
  const bg = useSharedValue(0);

  const tap = Gesture.Tap()
    .onBegin(() => {
      scale.value = withSpring(0.92);
    })
    .onFinalize(() => {
      scale.value = withSpring(1);
    });

  const longPress = Gesture.LongPress()
    .minDuration(600)
    .onStart(() => {
      scale.value = withSpring(1.15);
    })
    .onEnd(() => {
      scale.value = withSpring(1);
    });

  // Tap болон LongPress-г хавсарга — аль нэг нь эхэлбэл нөгөө нь зогсоно
  const composed = Gesture.Exclusive(longPress, tap);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  return (
    <GestureDetector gesture={composed}>
      <Animated.View style={[styles.card, animatedStyle]}>
        <Text style={styles.text}>Дар эсвэл удаан дар</Text>
      </Animated.View>
    </GestureDetector>
  );
}

const styles = StyleSheet.create({
  card: {
    width: 220,
    height: 100,
    backgroundColor: '#0f172a',
    borderRadius: 16,
    justifyContent: 'center',
    alignItems: 'center',
    borderWidth: 1,
    borderColor: '#22d3ee',
  },
  text: { color: '#f1f5f9', fontSize: 16 },
});

Pan gesture — чирэх

Gesture.Pan() нь хуруугаар чирэх үйлдлийг илрүүлж координатыг өгдөг:

typescript
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';
import { StyleSheet, View } from 'react-native';

export default function DraggableCard() {
  const translateX = useSharedValue(0);
  const translateY = useSharedValue(0);
  const startX = useSharedValue(0);
  const startY = useSharedValue(0);

  const pan = Gesture.Pan()
    .onStart(() => {
      startX.value = translateX.value;
      startY.value = translateY.value;
    })
    .onUpdate((event) => {
      translateX.value = startX.value + event.translationX;
      translateY.value = startY.value + event.translationY;
    })
    .onEnd(() => {
      // Дуусахад анхны байрлал руу буцна
      translateX.value = withSpring(0);
      translateY.value = withSpring(0);
    });

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [
      { translateX: translateX.value },
      { translateY: translateY.value },
    ],
  }));

  return (
    <View style={styles.container}>
      <GestureDetector gesture={pan}>
        <Animated.View style={[styles.draggable, animatedStyle]} />
      </GestureDetector>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#0b1120',
  },
  draggable: {
    width: 120,
    height: 120,
    backgroundColor: '#a78bfa',
    borderRadius: 16,
  },
});

Pinch gesture — томруулах, жижигрүүлэх

Хоёр хуруугаар нугалах Gesture.Pinch() нь зураг эсвэл контент томруулахад хэрэглэнэ:

typescript
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';
import { StyleSheet, Text } from 'react-native';

export default function PinchExample() {
  const scale = useSharedValue(1);
  const savedScale = useSharedValue(1);

  const pinch = Gesture.Pinch()
    .onUpdate((event) => {
      scale.value = savedScale.value * event.scale;
    })
    .onEnd(() => {
      savedScale.value = scale.value;
      // Хэт жижиг эсвэл том болвол хэвийн хэмжээнд буцна
      if (scale.value < 0.5) {
        scale.value = withSpring(0.5);
        savedScale.value = 0.5;
      }
      if (scale.value > 3) {
        scale.value = withSpring(3);
        savedScale.value = 3;
      }
    });

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  return (
    <GestureDetector gesture={pinch}>
      <Animated.View style={[styles.image, animatedStyle]}>
        <Text style={styles.emoji}>🖼️</Text>
      </Animated.View>
    </GestureDetector>
  );
}

const styles = StyleSheet.create({
  image: {
    width: 200,
    height: 200,
    backgroundColor: '#1e293b',
    borderRadius: 16,
    justifyContent: 'center',
    alignItems: 'center',
  },
  emoji: { fontSize: 80 },
});

Gesture Handler болон Reanimated хоёр нь хамтдаа React Native-н хамгийн хүчирхэг UI хэрэгслүүдийн нэг юм. Энэ хоёрыг эзэмшсэн бол ямар ч мэргэжлийн animation, interaction хийх чадвартай болно — өөрийгөө магтаарай!

Дараагийн хичээлд:

Гүйцэтгэлийн оновчлол — аппын хурдыг нэмэгдүүлэх, хоцролтыг бууруулах арга техникүүдийг авч үзнэ. memo, useMemo, useCallback, FlatList оновчлол зэрэг практик аргуудыг сурна.