实现对现有动画标签栏的反应导航
Implement react navigation to an existing animated tabbar
我有一个带有 react-native-reanimated 的自定义动画标签栏组件。我试图通过反应导航来实现它但是当我做动画时不起作用。对添加内容以使其正常运行的任何帮助。
这是组件
选项卡组件
interface TabProps {
children: ReactElement;
onPress: () => void;
// navigation: NavigationStackScreenProps<any, any>;
active: Animated.Node<number>;
transition: Animated.Node<number>;
index: number;
label: string;
}
const styles = StyleSheet.create({
icon: {
overflow: 'hidden',
},
});
export default ({
children,
active,
transition,
index,
onPress,
}: TabProps) => {
const isActive = eq(active, index);
const activeTransition = withTransition(isActive, {duration: DURATION});
const isGoingLeft = greaterThan(transition, active);
const width = interpolateNode(activeTransition, {
inputRange: [0, 1],
outputRange: [0, ICON_SIZE],
});
const direction = cond(
isActive,
cond(isGoingLeft, 'rtl', 'ltr'),
cond(isGoingLeft, 'ltr', 'rtl'),
);
return (
<TouchableWithoutFeedback {...{onPress}}>
<Animated.View
style={{
width: ICON_SIZE,
height: ICON_SIZE,
direction
}}>
<View style={StyleSheet.absoluteFill}>{children}</View>
<Animated.View style={[styles.icon, {width}]}>
{cloneElement(children, {active: true})}
</Animated.View>
</Animated.View>
</TouchableWithoutFeedback>
);
};
标签栏索引:
const tabs = [
{icon: <Home />, label: 'Home'},
{icon: <User />, label: 'Video'},
{icon: <Tv />, label: 'LiveStream'},
{icon: <Wallet />, label: 'Signin'},
];
const styles = StyleSheet.create({
container: {
backgroundColor: '#F0F0F6',
width: '90%',
position: 'absolute',
bottom: 20,
left: '5%',
borderRadius: 10,
},
tabs: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
tab: {
width: SEGMENT / 1.2,
height: ICON_SIZE + PADDING * 2,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
});
export default ({...props}) => {
const active = new Value<number>(0);
let navigation = useNavigation();
const transition = withTransition(active, {duration: DURATION});
const activeTransition = new Value(0);
useCode(
() =>
block([
onChange(active, set(activeTransition, 0)),
set(activeTransition, timing({duration: DURATION})),
]),
[active, activeTransition],
);
return (
<SafeAreaView style={styles.container}>
<View style={styles.tabs}>
{tabs.map(({icon, label}, index) => (
<View key={index} style={styles.tab}>
<Weave {...{active, transition, index}} />
<Tab
onPress={() => {
active.setValue(index);
console.log(active);
// title.setValue(label);
props.navigation.navigate(label);
}}
{...{active, transition, index, label}}>
{icon}
</Tab>
</View>
))}
<Particules {...{transition, activeTransition}} />
</View>
</SafeAreaView>
);
};
这是我试图复制的运球示例https://dribbble.com/shots/5380015-WeChat-Tab-Bar-Redesign
您使用的 react-native-reanimated
和 react-native
是什么版本?
我发现有几件事应该会导致问题,但我也不完全确定哪些内容适用于您的版本。
我看到的最重要的事情是您在组件的每个渲染器上实例化新的 Value
对象,如果尝试以功能方式设置动画,这应该会导致问题。稍微研究 react-native-reanimated
似乎有一些挂钩 (useValue
and useSharedValue
) 应该为您提供一个一致的对象,您可以在适用的地方使用。看起来您需要使用 useValue
.
由于动画会给您带来麻烦,因此使用 the examples provided in their fundamentals as good reference for side-by-side comparison. This could be especially useful if you had/have it working as a class component to compare the differences. As well as their useAnimatedStyle
钩子来了解它们如何处理功能组件中的动画可能是个好主意。
其他一些注意事项
- 您可能需要将
timing
调用替换为 withTiming
. timing
似乎特定于 v1
- 而不是使用
set
,您可能想要 useSharedValue
并只分配给共享值的 value
属性。 set
似乎也专门针对 reanimated 的 v1
// useSharedValue is provided by "react-native-reanimated"
const active = useSharedValue(0);
const activeTransition = useSharedValue(0);
// useEffect is provided by "react"
useEffect(() => {
// Set activeTransition to 0
activeTransition.value = 0;
}, [active.value]); // When active.value changes
希望这对您有所帮助
我有一个带有 react-native-reanimated 的自定义动画标签栏组件。我试图通过反应导航来实现它但是当我做动画时不起作用。对添加内容以使其正常运行的任何帮助。 这是组件 选项卡组件
interface TabProps {
children: ReactElement;
onPress: () => void;
// navigation: NavigationStackScreenProps<any, any>;
active: Animated.Node<number>;
transition: Animated.Node<number>;
index: number;
label: string;
}
const styles = StyleSheet.create({
icon: {
overflow: 'hidden',
},
});
export default ({
children,
active,
transition,
index,
onPress,
}: TabProps) => {
const isActive = eq(active, index);
const activeTransition = withTransition(isActive, {duration: DURATION});
const isGoingLeft = greaterThan(transition, active);
const width = interpolateNode(activeTransition, {
inputRange: [0, 1],
outputRange: [0, ICON_SIZE],
});
const direction = cond(
isActive,
cond(isGoingLeft, 'rtl', 'ltr'),
cond(isGoingLeft, 'ltr', 'rtl'),
);
return (
<TouchableWithoutFeedback {...{onPress}}>
<Animated.View
style={{
width: ICON_SIZE,
height: ICON_SIZE,
direction
}}>
<View style={StyleSheet.absoluteFill}>{children}</View>
<Animated.View style={[styles.icon, {width}]}>
{cloneElement(children, {active: true})}
</Animated.View>
</Animated.View>
</TouchableWithoutFeedback>
);
};
标签栏索引:
const tabs = [
{icon: <Home />, label: 'Home'},
{icon: <User />, label: 'Video'},
{icon: <Tv />, label: 'LiveStream'},
{icon: <Wallet />, label: 'Signin'},
];
const styles = StyleSheet.create({
container: {
backgroundColor: '#F0F0F6',
width: '90%',
position: 'absolute',
bottom: 20,
left: '5%',
borderRadius: 10,
},
tabs: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
tab: {
width: SEGMENT / 1.2,
height: ICON_SIZE + PADDING * 2,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
});
export default ({...props}) => {
const active = new Value<number>(0);
let navigation = useNavigation();
const transition = withTransition(active, {duration: DURATION});
const activeTransition = new Value(0);
useCode(
() =>
block([
onChange(active, set(activeTransition, 0)),
set(activeTransition, timing({duration: DURATION})),
]),
[active, activeTransition],
);
return (
<SafeAreaView style={styles.container}>
<View style={styles.tabs}>
{tabs.map(({icon, label}, index) => (
<View key={index} style={styles.tab}>
<Weave {...{active, transition, index}} />
<Tab
onPress={() => {
active.setValue(index);
console.log(active);
// title.setValue(label);
props.navigation.navigate(label);
}}
{...{active, transition, index, label}}>
{icon}
</Tab>
</View>
))}
<Particules {...{transition, activeTransition}} />
</View>
</SafeAreaView>
);
};
这是我试图复制的运球示例https://dribbble.com/shots/5380015-WeChat-Tab-Bar-Redesign
您使用的 react-native-reanimated
和 react-native
是什么版本?
我发现有几件事应该会导致问题,但我也不完全确定哪些内容适用于您的版本。
我看到的最重要的事情是您在组件的每个渲染器上实例化新的 Value
对象,如果尝试以功能方式设置动画,这应该会导致问题。稍微研究 react-native-reanimated
似乎有一些挂钩 (useValue
and useSharedValue
) 应该为您提供一个一致的对象,您可以在适用的地方使用。看起来您需要使用 useValue
.
由于动画会给您带来麻烦,因此使用 the examples provided in their fundamentals as good reference for side-by-side comparison. This could be especially useful if you had/have it working as a class component to compare the differences. As well as their useAnimatedStyle
钩子来了解它们如何处理功能组件中的动画可能是个好主意。
其他一些注意事项
- 您可能需要将
timing
调用替换为withTiming
.timing
似乎特定于 v1 - 而不是使用
set
,您可能想要useSharedValue
并只分配给共享值的value
属性。set
似乎也专门针对 reanimated 的 v1
// useSharedValue is provided by "react-native-reanimated"
const active = useSharedValue(0);
const activeTransition = useSharedValue(0);
// useEffect is provided by "react"
useEffect(() => {
// Set activeTransition to 0
activeTransition.value = 0;
}, [active.value]); // When active.value changes
希望这对您有所帮助