Xin chào ! Khi mới bắt đầu tìm hiểu về React Native, chắc hắn các bạn tò mò làm thế nào có thể thực hiện một animation trên React Native ?
Bài viết này sẽ giúp các bạn nắm rõ cách để tạo một animation căn bản và dùng trong ứng dụng của mình. Trước khi đọc, bạn nên xem qua documents của các lập trình viên tâm huyết code nên thư viện này để hiểu rõ cách animation vận hành và dùng bài viết này để nắm rõ bố cục của một animation.
Đây là trang website chính thức về React Native Animated:
Animated: https://reactnative.dev/docs/animated
Thread Model: https://reactnative.dev/architecture/threading-model
Giới thiệu về React Native Reanimated
Trong bối cảnh không ngừng phát triển của React Native, các lập trình viên liên tục tìm kiếm các cách để nâng cao hiệu suất và trải nghiệm người dùng của các ứng dụng di động của họ. React Native Reanimated là một thư viện mạnh mẽ được thiết kế để mang lại hiệu suất cao cho React Native Application. Trong bài viết này, chúng ta sẽ tìm hiểu cách tạo animation cơ bản từ React native reanimated.
Bước 1: Khởi tạo giá trị cho animation (Create animation's value contain )
useSharedValue()
Share Value là giá trị thay đổi trong react-native-reanimated. Bạn có thể nghĩ về nó như state của React, được tự động đồng bộ giữa phần "JavaScript" và phần "native" của ứng dụng của bạn (vì thế mà có tên gọi như vậy). Bạn tạo giá trị chia sẻ bằng cách sử dụng hook useSharedValue:
import { useSharedValue } from 'react-native-reanimated'; const mySharedValue = useSharedValue(initialValue);
Step 2: Tuỳ chỉnh animation (Config the animation)
withDecay
- Điều chỉnh tốc độ, vận tốc và điểm dừng của animation tuỳ theo hành động của user.
- Ví dụ: khi bạn lướt nhanh hay chậm thì animation sẽ phản ứng tuỳ theo tốc độ lướt của bạn.
withTiming
- Animation phổ biến để điều chỉnh duration (animation mất bao lâu để chạy xong) và easing (animation sẽ chuyển động như thế nào) của animation
- Mặc định easing của timing là easeInOut
withSpring
- Tương tự như Animted.timing nhưng sẽ cho bounce effect (animation nảy lên nảy xuống).
- Có thể điều chỉnh độ nảy theo:
- stiffness: The spring stiffness coefficient. Default 100.
- damping: Defines how the spring’s motion should be damped due to the forces of friction. Default 10.
- mass: The mass of the object attached to the end of the spring. Default 1.
Biến đổi giá trị (Interpolation)
- Cho phép tạo giá trị dựa vào một giá trị animation theo khoảng đầu vào và khoảng đầu ra.
- Ví dụ tạo một giá trị rotate (tranform rotate) từ giá trị đầu vào là number.
const size = interpolate(animation.value, [0, 1], [100, 250]);
Step 3: Sử dụng useAnimatedStyle
useAnimatedStyle cho phép bạn tạo một đối tượng styles, tương tự như styles của StyleSheet, có thể được animation bằng cách sử dụng các share value.
Các styles được định nghĩa bằng useAnimatedStyle cần phải được truyền vào thuộc tính style của một thành phần Animated. Các styles này sẽ tự động được cập nhật mỗi khi một share value hoặc state React liên quan thay đổi.
Trái ngược với việc sử dụng kiểu dáng inline, useAnimatedStyle cho phép truy cập các giá trị được lưu trữ trong share value trong đối tượng styles mà nó định nghĩa.
Để animation các thuộc tính, hãy sử dụng useAnimatedProps thay thế.
Dưới đây là một ví dụ về cách sử dụng useAnimatedStyle:
const animatedCard = useAnimatedStyle(() => { const size = interpolate(animation.value, [0, 1], [100, 250]); return { transform: [ { scale: size }, { translateY: card.value, }, ], opacity: selectedIndex === index ? 1 : 0.5, }; });
Step 4: Animated components
- Vậy có animation rồi thì làm thế nào để thêm vào một component để sử dụng ? React Native cụng cấp 2 giải pháp:
Default Animated
- Animated.Image
- Animated.ScrollView
- Animated.Text
- Animated.View
- Animated.FlatList
- Animated.SectionList
Custom Animated
Sử dụng createAnimatedComponent() để tạo một animated component từ một component bất kì.
Ví dụ
import { Image, ImageProps, TouchableWithoutFeedback,
} from 'react-native';
import { styles } from './Card.styles';
import Animated, { interpolate, useAnimatedStyle, useSharedValue, withDelay, withSpring,
} from 'react-native-reanimated';
import { useEffect } from 'react'; type Props = { cardImage: ImageProps; index: number; selectedIndex: number; cardLength: number; onPress: () => void;
}; export default function Card({ cardImage, index, selectedIndex, cardLength, onPress,
}: Props) { const delay = index * 200; const initValue = index * 10; const maxValue = (cardLength - 1) * 10; const card = useSharedValue(selectedIndex === 0 ? 0 : selectedIndex * 10); const animatedCard = useAnimatedStyle(() => { const size = interpolate(card.value, [0, maxValue], [1, 0.8]); console.log(size, card.value, index); return { transform: [ { scale: size }, { translateY: card.value, }, ], opacity: selectedIndex === index ? 1 : 0.5, }; }); useEffect(() => { card.value = withDelay(delay, withSpring(initValue)); }, []); return ( <TouchableWithoutFeedback onPress={onPress} style={[styles.imageWrapper, styles.container]} > <Animated.View style={[animatedCard, styles.container]}> <Image source={cardImage} style={styles.image} /> </Animated.View> </TouchableWithoutFeedback> );
}