I'm trying to build this sticky header navbar in my RN app. Basically, an horizontal scrollview of categories that highlight the current category based on Y scrolling.
Thanks to the video of great William Candillon (https://www.youtube.com/watch?v=xutPT1oZL2M&t=1369s) I'm pretty close, but I have a main problem.
I'm using interpolation to translate the X position of category View while scrolling. And then I have a Scrollview wrapping this Animated View. The problem is that Scrollview is not functional as is does not have the reference of the position of the Animated View. As you can see in the gif below (blue -> Animated.View / red -> Scrollview)
I like the interpolation approach as it's declarative and runs on native thread, so I tried to avoid as much as possible create listener attached to scrollTo() function.
What approach would you consider?
export default ({ y, scrollView, tabs }) => {
const index = new Value(0);
const [measurements, setMeasurements] = useState(
new Array(tabs.length).fill(0)
);
const indexTransition = withTransition(index);
const width = interpolate(indexTransition, {
inputRange: tabs.map((_, i) => i),
outputRange: measurements
});
const translateX = interpolate(indexTransition, {
inputRange: tabs.map((_tab, i) => i),
outputRange: measurements.map((_, i) => {
return (
-1 *
measurements
.filter((_measurement, j) => j < i)
.reduce((acc, m) => acc + m, 0) -
8 * i
);
})
});
const style = {
borderRadius: 24,
backgroundColor: 'black',
width,
flex: 1
};
const maskElement = <Animated.View {...{ style }} />;
useCode(
() =>
block(
tabs.map((tab, i) =>
cond(
i === tabs.length - 1
? greaterOrEq(y, tab.anchor)
: and(
greaterOrEq(y, tab.anchor),
lessOrEq(y, tabs[i + 1].anchor)
),
set(index, i)
)
)
),
[index, tabs, y]
);
return (
<Animated.View style={[styles.container, {}]}>
<Animated.ScrollView
scrollEventThrottle={16}
horizontal
style={{ backgroundColor: 'red', flex: 1 }}
>
<Animated.View
style={{
transform: [{ translateX }],
backgroundColor: 'blue'
}}
>
<Tabs
onPress={i => {
if (scrollView) {
scrollView.getNode().scrollTo({ y: tabs[i].anchor + 1 });
}
}}
onMeasurement={(i, m) => {
measurements[i] = m;
setMeasurements([...measurements]);
}}
{...{ tabs, translateX }}
/>
</Animated.View>
</Animated.ScrollView>
</Animated.View>
);
};