![]()
Ở 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

⚠️ 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ũ!

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ý:
onBegin: Khi ngón tay vừa chạm vào hộp.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).onFinalize: Khi nhấc ngón tay lên. (Lúc này ta dùngwithSpringđể 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.

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-handler và Reanimated 3 là bộ đôi hoàn hảo để xử lý UI hiện đại:
- Luôn khai báo
<GestureHandlerRootView>ở file ngoài cùng. - Dùng
GestureDetectorbọc lấy phần tử cần tương tác. - Cập nhật các biến
useSharedValuebê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!