UNPKG

@td-design/react-native-tabs

Version:

基于 @td-design/react-native 的 tabs 组件

145 lines (144 loc) 4.5 kB
import React, { useEffect, useMemo, useRef } from 'react'; import { Animated, Platform, StyleSheet } from 'react-native'; import { Flex, helpers } from '@td-design/react-native'; import { useMemoizedFn, useSafeState } from '@td-design/rn-hooks'; import TabBarIndicator from './TabBarIndicator'; import TabBarItem from './TabBarItem'; const { ONE_PIXEL, deviceWidth } = helpers; export default function TabBar(_ref) { let { tabs, onTabPress, onTabsLayout, height, position, offset, page, isIdle, scrollState, showIndicator = true, tabStyle, tabItemStyle, labelStyle, indicatorStyle } = _ref; const layouts = useRef([]); const indicatorWidth = getIndicatorWidth(indicatorStyle); const inputRange = useMemo(() => tabs.map((_, i) => i), [tabs]); const [outputRange, setOutputRange] = useSafeState(inputRange.map(() => 0)); const offsetPosition = Animated.add(position, offset); const scrollX = offsetPosition.interpolate({ inputRange, outputRange, extrapolate: 'clamp' }); const lastPage = useLastPage(page, isIdle); const interactive = useInteractive(scrollState); const handleTabPress = useMemoizedFn(index => { if (isIdle) { onTabPress(index); } }); const handleTabLayout = useMemoizedFn((e, index) => { layouts.current[index] = e.nativeEvent.layout; const length = layouts.current.filter(layout => layout.width > 0).length; if (length !== tabs.length) return; const range = []; for (let index = 0; index < length; index++) { const layout = layouts.current[index]; // 指示器要和当前Tab垂直居中对齐 const tabCenterX = layout.x + layout.width / 2; const indicatorX = tabCenterX - indicatorWidth / 2; range.push(indicatorX); } setOutputRange(range); onTabsLayout === null || onTabsLayout === void 0 ? void 0 : onTabsLayout(layouts.current); }); return /*#__PURE__*/React.createElement(Flex, { minWidth: deviceWidth, height: height, justifyContent: 'space-evenly', alignItems: 'center', backgroundColor: 'white', borderBottomColor: 'border', borderBottomWidth: ONE_PIXEL, style: tabStyle }, tabs.map((tab, index) => { const enhanced = interactive || index === page || index === lastPage; let scale = offsetPosition.interpolate({ inputRange: [index - 1, index, index + 1], outputRange: [1, enhanced ? 1.2 : 1, 1], extrapolate: 'clamp' }); let opacity = offsetPosition.interpolate({ inputRange: [index - 1, index, index + 1], outputRange: [0.8, enhanced ? 1 : 0.8, 0.8], extrapolate: 'clamp' }); if (Platform.OS === 'ios' && Math.abs(page - lastPage) > 1 && index === lastPage) { scale = offsetPosition.interpolate({ inputRange: [page - 1, page, page + 1], outputRange: [1.2, 1, 1.2], extrapolate: 'clamp' }); opacity = offsetPosition.interpolate({ inputRange: [page - 1, page, page + 1], outputRange: [1, 0.8, 1], extrapolate: 'clamp' }); } return /*#__PURE__*/React.createElement(TabBarItem, { key: tab, title: tab, onPress: () => handleTabPress(index), onLayout: e => handleTabLayout(e, index), style: tabItemStyle, labelStyle: [labelStyle, { opacity, transform: [{ scale }] }] }); }), showIndicator && /*#__PURE__*/React.createElement(TabBarIndicator, { style: [{ width: indicatorWidth }, indicatorStyle], scrollX: scrollX })); } const useLastPage = (page, isIdle) => { const lastPage = useRef(0); useEffect(() => { if (isIdle) { lastPage.current = page; } }, [page, isIdle]); return lastPage.current; }; const useInteractive = scrollState => { const interactive = useRef(false); const scrollStateRef = useRef(scrollState); useEffect(() => { scrollStateRef.current = scrollState; }, [scrollState]); if (scrollState === 'dragging') { interactive.current = true; } else if (scrollState === 'idle' && (Platform.OS === 'android' || scrollStateRef.current === 'settling')) { interactive.current = false; } return interactive.current; }; function getIndicatorWidth(style) { const flattenedStyle = StyleSheet.flatten([{ width: 24 }, style]); if (typeof flattenedStyle.width === 'number') { return flattenedStyle.width; } return 24; } //# sourceMappingURL=TabBar.js.map