Command Palette

Search for a command to run...

React Native Gesture Handler: Hướng dẫn bắt cử chỉ vuốt kéo thả

React Native Gesture Handler

Ở bài trước, chúng ta đã dùng Reanimated 3 để tạo ra những hoạt ảnh (Animation) siêu mượt mà. Tuy nhiên, các hoạt ảnh đó đa phần chỉ được kích hoạt bởi một cú "Chạm" (Tap) đơn giản thông qua nút bấm Pressable.

Nhưng điện thoại thông minh được sinh ra là để vuốt, kéo, thả và thu phóng. Hãy nghĩ đến thao tác quẹt thẻ để chọn "Thích" hoặc "Bỏ qua" trên Tinder, thao tác dùng 2 ngón tay để phóng to bản đồ, hay việc kéo một khung chat thả trôi nổi trên màn hình. Bạn không thể làm những điều đó chỉ với một cái chạm!

Để bắt được những chuyển động liên tục của ngón tay người dùng, chúng ta cần một công cụ chuyên biệt. Chào mừng bạn đến với "quyền năng" của thư viện React Native Gesture Handler.

1. Tại sao không dùng công cụ có sẵn của React Native?

React Native thực chất có cung cấp sẵn một công cụ tên là PanResponder để theo dõi thao tác vuốt.

Tuy nhiên, nó gặp phải một vấn đề chí mạng y hệt như Animated API đời cũ: Nút thắt cổ chai ở JS Thread. Mỗi khi ngón tay bạn di chuyển dù chỉ 1 pixel, PanResponder phải gửi dữ liệu tọa độ về cho JavaScript tính toán, sau đó JavaScript lại gửi lệnh vẽ sang UI Thread. Việc giao tiếp qua lại liên tục 60 lần/giây này khiến thao tác vuốt trên các máy Android đời cũ bị giật lag kinh khủng.

Giải pháp: Thư viện react-native-gesture-handler ra đời. Nó làm việc trực tiếp dưới tầng Native (UI Thread), kết hợp hoàn hảo với Reanimated 3 để tạo ra trải nghiệm kéo thả mượt mà 100% không độ trễ.

2. Cài đặt và cấu hình bắt buộc

Nếu bạn đã cài đặt môi trường ở những bài trước (khi làm Drawer Navigation), thư viện này có thể đã tồn tại trong máy bạn. Nếu chưa, hãy chạy lệnh sau trong Terminal:

npx expo install react-native-gesture-handler

React Native Gesture Handler

⚠️ LƯU Ý: Để thư viện này hoạt động, toàn bộ ứng dụng của bạn phải được bọc bên trong một thẻ <GestureHandlerRootView>. Hãy mở file cao nhất của dự án (ví dụ app/_layout.tsx nếu dùng Expo Router) và bọc nó lại:

import { GestureHandlerRootView } from 'react-native-gesture-handler'

export default function RootLayout() {
  return (
    // Thẻ này phải set flex: 1 để chiếm toàn bộ màn hình
    <GestureHandlerRootView style={{ flex: 1 }}>{/* Các màn hình ứng dụng của bạn */}</GestureHandlerRootView>
  )
}

Nếu bạn quên bước này, mọi code vuốt chạm bên dưới sẽ hoàn toàn bị tê liệt!

3. Các loại cử chỉ phổ biến nhất

Thư viện cung cấp một API hiện đại tên là Gesture, chứa đầy đủ các loại cử chỉ bạn cần:

  • Gesture.Tap(): Chạm (nhanh hơn và nhạy hơn Pressable thông thường).
  • Gesture.Pan(): Vuốt, kéo thả (Dịch chuyển theo trục X, Y).
  • Gesture.Pinch(): Dùng 2 ngón tay thu phóng (Zoom).
  • Gesture.Rotation(): Xoay phần tử bằng 2 ngón tay.
  • Gesture.LongPress(): Nhấn giữ.

4. Thực hành: Làm một chiếc hộp "Drag & Drop"

Hãy kết hợp Gesture.Pan() với Reanimated 3 để tạo ra một chiếc hộp có thể dùng ngón tay kéo đi khắp màn hình, và khi thả tay ra, nó sẽ tự động nảy lò xo bay về vị trí cũ!

React Native Drag & Drop

Bước 1: Khởi tạo biến lưu trữ tọa độ

Chúng ta cần 2 biến useSharedValue để lưu vị trí hiện tại của chiếc hộp trên trục X (ngang) và Y (dọc).

Bước 2: Bắt sự kiện Pan

Cử chỉ Pan (Vuốt) có 3 trạng thái quan trọng mà bạn phải xử lý:

  1. onBegin: Khi ngón tay vừa chạm vào hộp.
  2. onUpdate: Khi ngón tay đang di chuyển kéo cái hộp đi. (Lúc này ta lấy khoảng cách ngón tay đã di chuyển cộng vào tọa độ của hộp).
  3. onFinalize: Khi nhấc ngón tay lên. (Lúc này ta dùng withSpring để hộp bay về tọa độ 0).

Hãy xem đoạn code "ma thuật" này:

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

export default function DragBoxScreen() {
  // 1. Lưu tọa độ X, Y (Bắt đầu ở vị trí 0)
  const translateX = useSharedValue(0)
  const translateY = useSharedValue(0)

  // 2. Định nghĩa Cử chỉ Kéo thả (Pan)
  const panGesture = Gesture.Pan()
    // Khi đang kéo: Cập nhật tọa độ của hộp bằng chính quãng đường ngón tay đã di chuyển
    .onUpdate((event) => {
      translateX.value = event.translationX
      translateY.value = event.translationY
    })
    // Khi thả tay ra: Cho hộp nảy lò xo bay về vị trí gốc (0, 0)
    .onFinalize(() => {
      translateX.value = withSpring(0)
      translateY.value = withSpring(0)
    })

  // 3. Kết nối tọa độ vào Style của chiếc hộp
  const animatedStyle = useAnimatedStyle(() => {
    return {
      transform: [{ translateX: translateX.value }, { translateY: translateY.value }],
    }
  })

  return (
    <View style={styles.container}>
      {/* 4. Dùng GestureDetector bọc bên ngoài thẻ Animated.View để lắng nghe cử chỉ */}
      <GestureDetector gesture={panGesture}>
        {/* Chiếc hộp của chúng ta */}
        <Animated.View style={[styles.box, animatedStyle]} />
      </GestureDetector>
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#2d3436' },
  box: { width: 100, height: 100, backgroundColor: '#00b894', borderRadius: 20 },
})

Khi chạy thử, bạn hãy đặt ngón tay vào ô màu xanh, kéo nó đi góc khác rồi nhấc tay lên. Cảm giác vật lý mượt mà đến khó tin sẽ lập tức chinh phục bạn!

5. Ứng dụng thực tế: Swipe to Delete

Bạn đã từng vào app Gmail hoặc Messenger, vuốt một dòng sang trái để hiện ra nút "Thùng rác" chưa? Đó là tính năng kinh điển của Mobile.

React Native Swipe to Delete

Logic hoàn toàn tương tự chiếc hộp ở trên. Tuy nhiên:

  • Bạn thiết lập giới hạn cho translateX (chỉ cho phép vuốt sang trái một đoạn tối đa -100px).
  • Nếu người dùng thả tay ra mà khoảng cách vuốt chưa tới -50px, nó sẽ lò xo bật ngược lại. Nếu vượt qua -50px, nó trượt hẳn sang trái để lộ nút Xóa màu đỏ nằm lót ở bên dưới thẻ View đó!

(Gợi ý: Thư viện react-native-gesture-handler có cung cấp sẵn một component tên là Swipeable để làm tính năng này cực nhanh mà không cần tự code từ đầu!).

Kết luận: Bộ đôi hoàn hảo để xử lý UI hiện đại

Sự kết hợp giữa react-native-gesture-handlerReanimated 3 là bộ đôi hoàn hảo để xử lý UI hiện đại:

  1. Luôn khai báo <GestureHandlerRootView> ở file ngoài cùng.
  2. Dùng GestureDetector bọc lấy phần tử cần tương tác.
  3. Cập nhật các biến useSharedValue bên trong hàm .onUpdate() của Gesture.

Đi được tới đây, bạn đã làm chủ hoàn toàn giao diện, dữ liệu và tương tác vật lý. Ứng dụng của bạn đã hoàn thiện 100% chức năng. Nhưng làm thế nào để đưa ứng dụng tuyệt vời này lên tay người dùng? Nếu app có bug khẩn cấp, làm sao để sửa ngay mà không cần bắt người dùng tải lại app mới trên Store?

Mời bạn đón xem những bài học thú vị tiếp theo!

Bài viết liên quan

React Native là gì? Tại sao nên dùng nó để lập trình Mobile?

Tìm hiểu sức mạnh của React Native và nền tảng Expo giúp bạn tạo ứng dụng di động nhanh chóng mà không cần cài đặt rắc rối.

Cách gọi API trong React Native: Hướng dẫn Fetch Data và xử lý JSON

Hướng dẫn chi tiết cách gọi API trong React Native. Nắm vững kỹ thuật sử dụng fetch, axios kết hợp với useEffect, useState để tải và hiển thị dữ liệu server mượt mà.

Animation trong React Native: Hướng dẫn toàn tập Reanimated 3

Hướng dẫn cách khắc phục lỗi giật lag UI bằng React Native Reanimated 3. Khám phá bí quyết tạo animation mượt mà 60fps với useSharedValue và withSpring.

Đóng gói ứng dụng React Native: Hướng dẫn build APK, AAB Và IPA

Hướng dẫn chi tiết cách đóng gói ứng dụng React Native (Build App). Khám phá sức mạnh của EAS Build để tạo file APK, AAB cho Android và IPA cho iOS hoàn toàn tự động.