Home > other >  How to detect if FlatList scroll is triggered by gesture or by scrollToOffset method?
How to detect if FlatList scroll is triggered by gesture or by scrollToOffset method?

Time:06-29

How can you tell if a flatlist scroll is triggered by a user gesture or by a scrollToOffset or scrollToIndex method?

I search for something like that... (not working)

  const onScroll=(event, isGesture)=>{
      if(!isGesture)return
    }

 <FlatList onScroll={onScroll}/>

CodePudding user response:

onMomentumScrollEnd only triggeres on user Scroll

const onMomentumScrollEnd=(event)=>{
      
    }

 <FlatList onMomentumScrollEnd={onScroll}/>

CodePudding user response:

You can create a ref that you toggle before you call scrollToOffset or scrollToIndex, and then toggle again after the scroll animatino completes.

let isGesture = useRef(true).current;

Check this ref's value in your onScroll function to determine what to do.

 const onScroll=e=>{
    if(!isGesture){
      console.log('inorganic scrolling!')
      return
    }
    console.log('Organic scrolling!')
    // do your scroll stuff
  }

The biggest issue you will encounter will be figuring out how long to wait before toggling the ref back to its default value. If you wait too long, then you may miss user scroll events, and if you wait too shortly, you will capture the end of the scrollTo animation. Sadly, FlatList doesnt reveal anything about its scroll animation duration so you kinda have to guess:

  const {width, height} = useWindowDimensions();
  let flatListRef = useRef({}).current;
  let currentIndex = useRef(0).current;
  const listHeight = height;
 // simulate inorganic scroll
  const randomScroll = ()=>{
    // set isGesture to false before doing inorganic scrolling
    isGesture = false;
    let index = Math.floor(getRandom(0,data.length));
    flatListRef?.scrollToIndex({index,viewPosition:0});
    // calculate time to wait before setting isGesture back to true
    let itemsToPass = Math.abs(currentIndex - index);
    let scrollDist = itemsToPass*listHeight;
    // i found that scrolling 613 pixels take about 750ms
    const baseDist = 613
    const baseDuration = 750;
    // i doubt this is the formula used to calculate
    // flatlist animation duration but it kinda works
    const estimatedAnimationDuration = Math.min(
      baseDuration**(1 (baseDist/baseDuration))**(itemsToPass/100),
      //  animation usually doesnt exceeds 1500ms
      1500
    );
    console.log('Passing',itemsToPass,'items will take',estimatedAnimationDuration)
    // wait for animation to finish
    setTimeout(()=>{
      isGesture=true
    },estimatedAnimationDuration)
    currentIndex = index
  }

Here's an example

EDIT

Alternatively, you could disable the scroll animation and provide a small amount of time for the offset:

 const randomScroll = () => {
    // set isGesture to false before doing inorganic scrolling
    isGesture = false;
    let index = Math.floor(getRandom(0, data.length));
    const scrollConfig = { 
      index,
      viewPosition: 0,
      animated:false,
    }
    flatListRef?.scrollToIndex(scrollConfig);
    // calculate time to wait before setting isGesture back to true
    let itemsToPass = Math.abs(currentIndex - index);
    let scrollDist = itemsToPass * listHeight;
    // i found that scrolling 613 pixels take about 750ms
    const baseDist = 613;
    const baseDuration = 750;
    // i doubt this is the formula used to calculate
    // flatlist animation duration but it kinda works
    const estimatedAnimationDuration = scrollConfig.animated 
      ? Math.min(
        baseDuration ** ((1   baseDist / baseDuration) ** (itemsToPass / 100)),
        //  animation usually doesnt exceeds 1500ms
        1500)
      : 200
    scrollConfig.animated && console.log(
      'Passing',
      itemsToPass,
      'items will take',
      estimatedAnimationDuration
    );
    // wait for animation to finish
    setTimeout(() => {
      isGesture = true;
    }, estimatedAnimationDuration);
    currentIndex = index;
  };

Demo this here

  • Related