Scroll-Linked Animation

Animated.event and Scroll-Linked Animation

Animated.event() takes a mapping that mirrors the event object and names the field that feeds each value. Pass the result to an animated component's onScroll with useNativeDriver: true, and the scroll view writes its offset into the value on the UI thread, with no JavaScript per frame. A reading progress bar:

src/anim/ReadingProgress.tsx: a progress bar driven by the scroll offset (excerpt)TSX
  const [maxY, setMaxY] = useState(1);            // content height minus viewport height
  const onScroll = Animated.event(
    [{ nativeEvent: { contentOffset: { y: scrollY } } }],   // event path -> value
    { useNativeDriver: true, listener: () => { calls.current += 1; } });
  const progress = scrollY.interpolate({ inputRange: [0, maxY], outputRange: [0, 1],
    extrapolate: 'clamp' });
// ...the bar, and the scroll view (which logs where each fling comes to rest):
        <Animated.View style={{ height: 6, backgroundColor: '#1565c0',
          transformOrigin: 'left', transform: [{ scaleX: progress }] }} />
      </View>
      <Animated.ScrollView onScroll={onScroll} scrollEventThrottle={16}
Output
 LOG  rest at y = 542 of 3160 | JS listener calls: 13

After one fling (adb shell input swipe), the bar stood at 17% while JavaScript heard of the scroll 13 times. It scales from its left edge instead of animating width, a layout prop the native driver cannot touch. For a header that hides on scroll down and returns on scroll up, use Animated.diffClamp(scrollY, 0, 56).