💡 Tại Sao Tôi Xây Dựng Điều Này
Khi điều hướng mặc định của React Navigation hoạt động ổn, tôi muốn tạo ra một trải nghiệm hình ảnh sống động nơi mà các màn hình phản ứng động với các tương tác của thanh điều hướng. Mục tiêu là tạo ra một điều hướng theo phong cách iOS với:
- Các xoay 3D mượt mà
- Các hoạt ảnh thu phóng/giả lập
- Tích hợp hồ sơ tùy chỉnh
- Điều khiển cử chỉ liền mạch
🧩 Công Nghệ Chính Được Sử Dụng
expo-router(Điều hướng dựa trên tệp)react-native-reanimated(Hoạt ảnh)@react-navigation/drawer(Điều hướng cốt lõi)react-native-gesture-handler(Cử chỉ vuốt)
🚀 Hướng Dẫn Triển Khai Từng Bước
1. Cài Đặt Cơ Bản
Để bắt đầu, bạn cần tạo một ứng dụng Expo mới và cài đặt các thư viện cần thiết:
npx create-expo-app@latest
npx expo install @react-navigation/drawer react-native-gesture-handler react-native-reanimated
2. Bọc Hoạt Ảnh Ma Thuật (drawer-scene-wrapper.tsx)
Component này sẽ làm cho các màn hình chuyển động khi thanh điều hướng mở:
tsx
const DrawerSceneWrapper = ({ children }: { children: ReactNode }) => {
const progress = useDrawerProgress();
const animatedStyled = useAnimatedStyle(() => ({
transform: [
{ scale: interpolate(progress.value, [0, 1], [1, 0.8]) },
{ translateX: interpolate(progress.value, [0, 1], [0, 170]) },
{ rotateY: `${interpolate(progress.value, [0, 1], [0, -25])}deg` }
],
borderRadius: 20
}));
return <Animated.View style={[styles.container, animatedStyled]}>
{children}
</Animated.View>;
};
3. Giao Diện Drawer Tùy Chỉnh (custom-drawer-content.tsx)
Thêm hồ sơ người dùng và nút đăng xuất:
tsx
<SafeAreaView style={{ flex: 1, backgroundColor: "transparent" }}>
{/* Phần Hồ Sơ */}
<TouchableOpacity style={styles.userContainer}>
<Image source={{ uri: "USER_AVATAR_URL" }} style={styles.userImage} />
<View>
<Text style={styles.userName}>Haider Mukhtar</Text>
<Text style={styles.userEmail}>haider@example.com</Text>
</View>
</TouchableOpacity>
{/* Các Mục Điều Hướng */}
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
</DrawerContentScrollView>
{/* Nút Đăng Xuất */}
<TouchableOpacity style={styles.logoutButton}>
<Ionicons name="log-out" size={24} color="#FFFFFF" />
<Text style={styles.logoutText}>Logout</Text>
</TouchableOpacity>
</SafeAreaView>
4. Cấu Hình Drawer (_layout.tsx)
Tùy chỉnh style cho drawer với các hiệu ứng:
tsx
<GestureHandlerRootView style={{ flex: 1 }}>
<Drawer
drawerContent={(props) => <CustomDrawerContent {...props} />}
screenOptions={{
headerShown: false,
drawerActiveBackgroundColor: "#33b3a6",
drawerInactiveBackgroundColor: "transparent",
drawerActiveTintColor: "#FFFFFF",
drawerInactiveTintColor: "#FFFFFF",
overlayColor: "transparent",
drawerStyle: {
backgroundColor: "transparent",
width: "60%",
paddingTop: 40,
},
drawerLabelStyle: {
marginLeft: -6,
fontSize: 18,
fontFamily: "PoppinsMedium500",
color: "#FFFFFF",
},
drawerItemStyle: {
marginLeft: -16,
marginRight: 35,
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
borderTopRightRadius: 50,
borderBottomRightRadius: 50,
},
sceneStyle: {
backgroundColor: "#26867C",
},
}}
>
<Drawer.Screen
name="index"
options={{
title: "Home",
drawerIcon: ({ color }) => <Ionicons name="home" color={color} />
}}
/>
{/* Thêm các màn hình khác ở đây */}
</Drawer>
</GestureHandlerRootView>
5. Triển Khai Màn Hình (index.tsx)
Gói mỗi màn hình trong DrawerSceneWrapper:
tsx
const HomeScreen = () => {
const navigation = useNavigation();
return (
<DrawerSceneWrapper>
<View style={styles.header}>
<TouchableOpacity
onPress={() => navigation.dispatch(DrawerActions.openDrawer())}
>
<Ionicons name="menu" size={28} color="#000" />
</TouchableOpacity>
<Text style={styles.headerTitle}>Home</Text>
</View>
{/* Nội dung màn hình */}
</DrawerSceneWrapper>
);
}
👀 Thời Gian Demo!
Dưới đây là cách mà nó hoạt động:
🎨 Điểm Nổi Bật Thiết Kế
- Xoay 3D và thu phóng động đồng bộ với các cử chỉ của drawer.
- Các mục menu hình bầu dục với hiệu ứng trạng thái hoạt động.
🚫 Những Cạm Bẫy Thường Gặp Đã Được Giải Quyết
- Xung đột cử chỉ: Đã bao bọc root trong
<GestureHandlerRootView>. - Hạn chế hoạt ảnh: Sử dụng
Extrapolation.CLAMPđể đảm bảo sự mượt mà. - Khu vực an toàn: Sử dụng
react-native-safe-area-contextđể hỗ trợ notch. - TypeScript: Kiểm tra kiểu nghiêm ngặt cho các props của drawer (
DrawerContentComponentProps).
📱 Thử Nghiệm Ngay!
Clone dự án đầy đủ:
👉 Github Repo: Animated-Drawer-Navigation-Expo
💬 Suy Nghĩ Cuối Cùng
Triển khai này chứng minh rằng với:
- Ma thuật nội suy của Reanimated ✨
- Công cụ không cần cấu hình của Expo 🧰
- Thiết kế chiến lược 🎨
… bạn có thể tạo ra một điều hướng mang lại niềm vui cho người dùng trong khi vẫn giữ mã nguồn đơn giản. Bạn sẽ thêm gì vào đây? Hãy chia sẻ ý tưởng của bạn dưới đây! 👇
🔗 Haider Mukhtar trên LinkedIn