import type { ViewStyle } from 'react-native' import * as React from 'react' import { Animated, Text, TouchableOpacity, View } from 'react-native' import PagerView from 'react-native-pager-view' export interface PagerRoute { key: string title: string element?: React.ReactNode } interface PagerTabsProps { routes: PagerRoute[] left?: React.ReactNode right?: React.ReactNode selectedIndex?: number // 外部控制选中页 onChange?: (key: string) => void indicatorWidth?: number className?: string style?: ViewStyle } export default function PagerTabs({ routes, left, right, selectedIndex, onChange, indicatorWidth = 38, className, style, }: PagerTabsProps) { const pagerRef = React.useRef(null) const [index, setIndex] = React.useState(selectedIndex ?? 0) const [tabWidth, setTabWidth] = React.useState(0) const scrollX = React.useRef(new Animated.Value(selectedIndex ?? 0)).current // 外部 selectedIndex 更新时同步内部 index React.useEffect(() => { if (typeof selectedIndex === 'number' && selectedIndex !== index) { setIndex(selectedIndex) pagerRef.current?.setPage(selectedIndex) } }, [selectedIndex]) const onTabPress = (i: number) => { pagerRef.current?.setPage(i) } const onPageScroll = (e: any) => { const { position, offset } = e.nativeEvent scrollX.setValue(position + offset) } const onPageSelected = (position: number) => { setIndex(position) if (onChange && routes[position]) { onChange(routes[position].key) } } const tabWidthPerTab = tabWidth / routes.length const indicatorTranslateX = scrollX.interpolate({ inputRange: routes.map((_, i) => i), outputRange: routes.map( (_, i) => i * tabWidthPerTab + (tabWidthPerTab - indicatorWidth) / 2, ), extrapolate: 'clamp', }) return ( {/* Header */} {left} {/* Tabs */} { const w = e.nativeEvent.layout.width if (w !== tabWidth) setTabWidth(w) }} > {routes.map((r, i) => ( onTabPress(i)} > {r.title} ))} {tabWidth > 0 && ( )} {right} {/* Pager */} onPageSelected(e.nativeEvent.position)} > {routes.map(r => ( {r.element ?? null} ))} ) }