Skip to content

Commit

Permalink
feat: make TabView work well
Browse files Browse the repository at this point in the history
  • Loading branch information
mahaaoo committed Sep 21, 2024
1 parent 073dfc8 commit bca87f7
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 28 deletions.
2 changes: 1 addition & 1 deletion src/components/PageView/PageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const PageView = forwardRef<PageViewRef, PageViewProps>((props, ref) => {
};

const pageScroll = (translate: number) => {
onPageScroll && onPageScroll(Math.abs(translate));
onPageScroll && onPageScroll(-translate);
};

const setPage = (index: number) => {
Expand Down
47 changes: 34 additions & 13 deletions src/components/TabBar/TabBar.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import React, { forwardRef, useImperativeHandle, useMemo, useRef } from 'react';
import { View, ScrollView, LayoutChangeEvent, StyleSheet } from 'react-native';
import TabBarItem from './TabBarItem';
import Animated, {
interpolate,
useAnimatedStyle,
useSharedValue,
withTiming,
} from 'react-native-reanimated';

import TabBarItem from './TabBarItem';
import TabBarSlider from './TabBarSlider';
import Separator from './Separator';

import { TabBarProps, TabBarItemLayout, TabBarRef } from './type';
import { useVerifyProps } from './hook';

Expand Down Expand Up @@ -38,18 +41,27 @@ const TabBar = forwardRef<TabBarRef, TabBarProps>((props, ref) => {
const scrollRef = useRef<ScrollView>(null);
const layouts = useRef<TabBarItemLayout[]>([]).current;
const sliderWidth = useRef(defalutSliderWidth);
const sliderOutput = useRef<number[]>([]);
const intput = tabs.map((_, index) => index);

useImperativeHandle(
ref,
() => ({
setTab: handleOnPress,
getCurrent: () => currentIndex.value,
syncCurrentIndex,
keepScrollViewMiddle,
}),
[]
);

const syncCurrentIndex = (offset: number) => {
currentIndex.value = offset;
sliderOffset.value = interpolate(offset, intput, sliderOutput.current);
};

const handleOnPress = (index: number) => {
currentIndex.value = index;
currentIndex.value = withTiming(index);
calculateSliderOffset(index);
onPress && onPress(index);
};
Expand All @@ -59,6 +71,11 @@ const TabBar = forwardRef<TabBarRef, TabBarProps>((props, ref) => {
const length = layouts.filter((layout) => layout.width > 0).length;

if (length === tabs.length) {
sliderOutput.current = layouts.map((layout: TabBarItemLayout) => {
const { x, y, width, height } = layout;
const toValue = x + (width - sliderWidth.current) / 2;
return toValue;
});
calculateSliderOffset(initialTab);
}
};
Expand All @@ -72,17 +89,8 @@ const TabBar = forwardRef<TabBarRef, TabBarProps>((props, ref) => {
sliderWidth.current = width;
};

const calculateSliderOffset = (index: number) => {
'worklet';
if (index < 0 || index >= tabs.length) {
throw new Error(
'calculateSliderOffset can only handle index [0, tabs.length - 1]'
);
}

const { x, y, width, height } = layouts[index];

const toValue = x + (width - sliderWidth.current) / 2;
const keepScrollViewMiddle = (index: number) => {
const toValue = sliderOutput.current[index];
if (toValue > contentSize / 2) {
scrollRef.current &&
scrollRef.current?.scrollTo({
Expand All @@ -94,6 +102,18 @@ const TabBar = forwardRef<TabBarRef, TabBarProps>((props, ref) => {
scrollRef.current &&
scrollRef.current?.scrollTo({ x: 0, y: 0, animated: true });
}
};

const calculateSliderOffset = (index: number) => {
'worklet';
if (index < 0 || index >= tabs.length) {
throw new Error(
'calculateSliderOffset can only handle index [0, tabs.length - 1]'
);
}

keepScrollViewMiddle(index);
const toValue = sliderOutput.current[index];
sliderOffset.value = withTiming(toValue);
};

Expand Down Expand Up @@ -134,6 +154,7 @@ const TabBar = forwardRef<TabBarRef, TabBarProps>((props, ref) => {
style={tabBarItemStyle}
titleStyle={tabBarItemTitleStyle}
index={index}
currentIndex={currentIndex}
title={tab}
onLayout={onTabBarItemLayout}
onPress={handleOnPress}
Expand Down
25 changes: 24 additions & 1 deletion src/components/TabBar/TabBarItem.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { Text, TouchableOpacity, StyleSheet } from 'react-native';
import { TabBarItemProps } from './type';
import Animated, { Extrapolation, interpolate, useAnimatedStyle } from 'react-native-reanimated';

const TabBarItem: React.FC<TabBarItemProps> = (props) => {
const {
Expand All @@ -9,17 +10,39 @@ const TabBarItem: React.FC<TabBarItemProps> = (props) => {
width = 'auto',
style,
titleStyle,
currentIndex,
onLayout,
onPress,
} = props;

const animatedText = useAnimatedStyle(() => {
return {
opacity: interpolate(
currentIndex.value,
[index - 1, index, index + 1],
[0.8, 1, 0.8],
Extrapolation.CLAMP
),
transform: [
{
scale: interpolate(
currentIndex.value,
[index - 1, index, index + 1],
[0.9, 1.1, 0.9],
Extrapolation.CLAMP
),
},
],
};
});

return (
<TouchableOpacity
onPress={() => onPress(index)}
onLayout={(event) => onLayout(index, event.nativeEvent.layout)}
style={[styles.container, { width: width }, style]}
>
<Text style={titleStyle}>{title}</Text>
<Animated.Text style={[titleStyle, animatedText]}>{title}</Animated.Text>
</TouchableOpacity>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/TabBar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import TabBar from './TabBar';

export { TabBarRef } from './type';
export { TabBar };
3 changes: 3 additions & 0 deletions src/components/TabBar/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ export interface TabBarProps {
export interface TabBarRef {
setTab: (index: number) => void;
getCurrent: () => number;
syncCurrentIndex: (offset: number) => void;
keepScrollViewMiddle: (index: number) => void;
}

export interface TabBarItemProps {
index: number;
currentIndex: SharedValue<number>;
title: string;
style?: ViewStyle;
titleStyle?: TextStyle;
Expand Down
16 changes: 5 additions & 11 deletions src/components/TabView/TabView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useRef } from 'react';
import { View, Text, Dimensions } from 'react-native';
import { TabBar } from '../TabBar';
import { TabBar, TabBarRef } from '../TabBar';
import { PageView, PageViewRef } from '../PageView';
import Animated, { useSharedValue } from 'react-native-reanimated';

Expand All @@ -15,7 +15,7 @@ const TabView: React.FC<TabViewProps> = (props) => {
// const contentSize = width;
const currentIndex = useSharedValue(2);
const pageRef = useRef<PageViewRef>(null);
const tabRef = useRef(null);
const tabRef = useRef<TabBarRef>(null);

return (
<Animated.View style={{ flex: 1 }}>
Expand All @@ -35,9 +35,7 @@ const TabView: React.FC<TabViewProps> = (props) => {
backgroundColor: 'orange',
}}
onPress={(index) => {
// pageRef.current && pageRef.current?.setPage(index);
console.log('onPress index', index);
// currentIndex.value = index;
pageRef.current && pageRef.current?.setPage(index);
}}
initialTab={currentIndex.value}
/>
Expand All @@ -46,17 +44,13 @@ const TabView: React.FC<TabViewProps> = (props) => {
ref={pageRef}
initialPage={currentIndex.value}
onPageSelected={(currentPage) => {
// console.log('currentPage:', currentPage);
// tabRef.current && tabRef.current?.setTab(currentPage);
tabRef.current && tabRef.current?.keepScrollViewMiddle(currentPage);
}}
onPageScrollStateChanged={(state) => {
// console.log('state:', state);
}}
onPageScroll={(translate) => {
// console.log('translate', translate);
// currentIndex.value = translate;
// const page = pageRef.current && pageRef.current?.getCurrentPage();
// console.log('translate', page);
tabRef.current && tabRef.current?.syncCurrentIndex(translate / width);
}}
scrollEnabled={true}
bounces={true}
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,4 @@ export { Freeze } from './utils/Freeze';
export { TabView } from './components/TabView';
export { HeadTabView } from './components/HeadTabView';
export { PageView, PageViewRef } from './components/PageView';
export { TabBar } from './components/TabBar';
export { TabBar, TabBarRef } from './components/TabBar';

0 comments on commit bca87f7

Please sign in to comment.