React Native ScrollView:如何实现逐项滚动及禁用惯性滚动并吸附?
Hey there! Let's break down your two React Native ScrollView questions with practical, actionable solutions—these are super common when building controlled, smooth scroll experiences.
The easiest way to get item-by-item scrolling is to leverage ScrollView's built-in pagingEnabled prop. When enabled, ScrollView will automatically snap to "pages" that match its own width (for horizontal) or height (for vertical). You just need to make sure each list item takes up the full dimension of the ScrollView.
Here's a quick example for horizontal scrolling:
import React from 'react'; import { ScrollView, View, Text, StyleSheet } from 'react-native'; const ItemByItemScroll = () => { const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4']; return ( <ScrollView horizontal pagingEnabled={true} showsHorizontalScrollIndicator={false} style={styles.scrollContainer} > {items.map((item, idx) => ( <View key={idx} style={styles.item}> <Text style={styles.itemText}>{item}</Text> </View> ))} </ScrollView> ); }; const styles = StyleSheet.create({ scrollContainer: { flex: 1, }, item: { width: '100%', // Match ScrollView's width to create a "page" per item justifyContent: 'center', alignItems: 'center', backgroundColor: '#f5f5f5', marginVertical: 8, }, itemText: { fontSize: 22, fontWeight: '600', }, }); export default ItemByItemScroll;
For vertical scrolling, simply remove the horizontal prop and set each item's height to match the ScrollView's height instead of width.
You're right—there's no single prop that directly does this, but we can combine a few props and a touch of event handling to get exactly the behavior you want: no inertia, and the list always snaps to the nearest item regardless of drag force.
Here's how to implement it:
import React, { useRef } from 'react'; import { ScrollView, View, Text, StyleSheet } from 'react-native'; const NoInertiaScroll = () => { const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4']; const scrollRef = useRef(null); let currentPage = 0; const handleScrollEnd = (event) => { const { contentOffset, layoutMeasurement } = event.nativeEvent; const pageSize = layoutMeasurement.width; // Use height for vertical const targetPage = Math.round(contentOffset.x / pageSize); // Use y for vertical // Force scroll to the exact target page to eliminate any residual inertia scrollRef.current?.scrollTo({ x: targetPage * pageSize, // Use y for vertical animated: true, }); currentPage = targetPage; }; return ( <ScrollView ref={scrollRef} horizontal pagingEnabled={true} decelerationRate={0} // This kills inertia entirely showsHorizontalScrollIndicator={false} onScrollEndDrag={handleScrollEnd} style={styles.scrollContainer} > {items.map((item, idx) => ( <View key={idx} style={styles.item}> <Text style={styles.itemText}>{item}</Text> </View> ))} </ScrollView> ); }; const styles = StyleSheet.create({ scrollContainer: { flex: 1, }, item: { width: '100%', justifyContent: 'center', alignItems: 'center', backgroundColor: '#f5f5f5', marginVertical: 8, }, itemText: { fontSize: 22, fontWeight: '600', }, }); export default NoInertiaScroll;
Key details here:
decelerationRate={0}: This makes the ScrollView stop instantly as soon as you release the drag, eliminating all inertia.pagingEnabled={true}: Provides the base snapping behavior.onScrollEndDraghandler: Ensures that even if there's any tiny residual movement, we manually scroll to the exact nearest item for perfect alignment.
If you find decelerationRate={0} feels too abrupt, you can try decelerationRate="fast" instead—it reduces inertia to a minimum while keeping a slight smoothness, and the scrollTo call will still ensure it snaps to the correct item.
内容的提问来源于stack exchange,提问作者Michał Urbaniak

