I am trying to create a custom Scroll Knob much like Google Photos to allow fast scrolling on a long list. Similar to How to use a Pan responder to scroll a scrollView (React)
I got the first part done which is onScroll set an animated value and then use interpolate to provide the final position for the knob.
const handleTopPosition = useRef(new Animated.Value(0)).current;
return (
<Animated.View
style={[
styles.container,
{
opacity:
headerHeight > 0
? contentOffsetPositionRef.current!.interpolate({
inputRange: [-52 - headerHeight, -headerHeight, 0],
outputRange: [0, 0.6, 0.6],
})
: 0.6,
transform: [
{
translateY: Animated.add(
handleTopPosition,
contentOffsetPositionRef.current!.interpolate({
inputRange:
headerHeight > 0
? [
-1000,
-headerHeight,
0,
contentHeight - viewLayout.height,
contentHeight,
]
: [
-1000,
0,
contentHeight - viewLayout.height,
contentHeight,
],
outputRange:
headerHeight > 0
? [
1000,
headerHeight,
headerHeight,
viewLayout.height - knobSize,
viewLayout.height,
]
: [
1000,
0,
viewLayout.height - knobSize,
viewLayout.height,
],
}),
),
},
],
},
]}
>
<Icon name="close" size={iconSize} />
</Animated.View>
);
Now I want to use the PanResponder to track scrolling via the scroll knob, so I want to do the inverse of that interpolation
const handleTopInterpolation = handleTopPosition.interpolate({
inputRange:
headerHeight > 0
? [-headerHeight, viewLayout.height - knobSize, viewLayout.height]
: [0, viewLayout.height - knobSize, viewLayout.height],
outputRange: [0, contentHeight - viewLayout.height, contentHeight],
});
I know I can compute the interpolation myself, but I wanted to use the existing interpolate
animation so utilize the computations done by Animated
useEffect(() => {
const h = handleTopPosition.addListener(({ value: distanceScrollKnobTravelled }) => {
const nextContentOffset =
distanceScrollKnobTravelled * ((contentHeight - viewLayout.height) / (viewLayout.height - knobSize)) + contentOffset;
// (note the following doesn't actually work I am still trying to find out why)
flashListRef.current?.scrollToOffset({
offset: nextContentOffset,
animated: false,
});
});
return () => handleTopPosition.removeListener(h);
}, [headerHeight, contentOffset]);
Anyway what I wanted was something like
const handleTopInterpolation = handleTopPosition.interpolate({
inputRange:
headerHeight > 0
? [-headerHeight, viewLayout.height - knobSize, viewLayout.height]
: [0, viewLayout.height - knobSize, viewLayout.height],
outputRange: [0, contentHeight - viewLayout.height, contentHeight],
});
useEffect(() => {
const h = handleTopInterpolation.addListener(
({ value: handleOffset }) => {
flashListRef.current?.scrollToOffset({
offset: nextContentOffset,
animated: false,
});
return () => handleTopInterpolation.removeListener(h);
}, [knobRatio, headerHeight]);
Where the handleTopInterpolation automatically updates.