Listening for changes in interpolated values?

47 views Asked by At

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.

0

There are 0 answers