React-native-interactable: onSnap is not being called instantly

Created on 2 Jul 2017  路  14Comments  路  Source: wix/react-native-interactable

Hi,

I have a use case where I need to receive the onSnap event faster than default behaviour.
Is there a way to control how fast an interactable view sends an onSnap event?

I tried to set view's velocity but it didn't help.

Thanks for the help!

bug

Most helpful comment

The issue I see here is that onSnap may be called with a big delay. Of course this is not intentional, onSnap show be called instantly.

We will investigate.

All 14 comments

Could you provide some more input on what you'd like to achieve?
I'm using a hack to remove an overlay instantly but don't know if this would be helpful to you.

I have a top panel that needs to be displayed when clicking on a button.
This panel has 4 snapPoints:

  • fully hidden (before clicking preview)
  • 2 intermediate points
  • max snap point

When the user open this panel to the maximum, the panel should become scrollable.
The issue is that I receive the onSnap event for the max snap point with a little delay as visible in the gif below (see warning at the bottom of the screen).
I am wondering if it is possible to control how fast the event for a snap point is sent.

interactable

<Interactable.View
        ref={thumbPreviewRef => {
          this.thumbPreviewRef = thumbPreviewRef;
        }}
        style={{
          position: 'absolute',
          left: 0,
          right: 0,
          top: 0
        }}
        verticalOnly
        initialPosition={{ y: -PANEL_HEIGHT }}
        snapPoints={[
          { y: -PANEL_HEIGHT, id: CLOSED_SNAP_POINT_ID },
          { y: -PANEL_HEIGHT + PADDING },
          { y: -Screen.width * (1 / 2) },
          { y: 0, id: MAX_OPENED_SNAP_POINT_ID }
        ]}
        boundaries={{ bottom: 0 }}
        animatedNativeDriver
        onSnap={event => {
          const { nativeEvent } = event;
          if (nativeEvent.id === CLOSED_SNAP_POINT_ID) {
            if (this.state.scrollEnabled) {
              this.setState({ scrollEnabled: false });
            }
            this.props.onClosed();
          } else if (nativeEvent.id === MAX_OPENED_SNAP_POINT_ID) {
            console.warn('Received max snap point event');
            if (!this.state.scrollEnabled) {
              this.setState({ scrollEnabled: true });
            }
          }
        }}
      >
        <View style={styles.panel}>
          <FlatList
            ref={flatListRef => (this.flatListRef = flatListRef)}
            data={this.props.data}
            bounces={false}
            directionalLockEnabled
            keyExtractor={(item, index) => index}
            scrollEnabled={this.state.scrollEnabled}
            onScroll={event => this.onScroll(event)}
            scrollEventThrottle={16}
            renderItem={({ item, index }) => {
              return (
                <Text>
                  <Text>
                    In in ut adipisicing fugiat ullamco anim aliqua cillum
                    labore esse commodo deserunt dolor consectetur. Id eu ipsum
                    esse pariatur id consectetur aliqua non. Veniam nulla irure
                    et cillum irure. Sint ea enim est eiusmod consectetur velit
                    do elit irure adipisicing pariatur.
                  </Text>
                  In velit pariatur id tempor. Magna deserunt enim amet minim.
                  Laborum est sint magna consectetur reprehenderit consectetur
                  ea consectetur culpa amet exercitation laborum officia
                  proident.
                </Text>
              );
            }}
          />
        </View>
      </Interactable.View>

// and the style
  panel: {
    height: PANEL_HEIGHT,
    backgroundColor: '#eee'
  },

@jnsone11 Please let me know if you have any solution :)

What you want to achieve is called nested scrolling and a big problem on android. Therefore the android sdk contains classes like CollapsibleLayout and NestedScrollView to achieve the effects you'd like to get.

I don't think interactible is the right tool (yet) for your requirements. I'd rather look into wrapping a native Collapsible Header + NestedScrollView. Maybe there are already libraries doing this for you.

That said I see two possible hacks to resolve the problem. By hacks I mean I don't really like these solutions but you might want to try it out.

1.
Put a static view above your scrollview inside the panel so that you cannot scroll it. Then add a transforms function to the overlay to remove it according to the panel position. Just translate X/Y by screen width/height for 99% of the possible panel positions. The other 1% translate 0 so that the scrollview cannot get touches. If you set your transform points exactly 1 pixel in front of the snapping point this "snaps" 100% immediately.

2.
Calculate your scrolling content total height. Make the content static, not scrolling. Add snap points according to content height / screen height so that the user can snap through the panel.

I'm looking forward to hearing your thoughts.

@jnsone11 Thanks for those precious details :)
I'll try have a look at this in the coming days and will give updates.

One thing I forgot to mention is that I try to implement this behavior only in iOS.
Is your comment still valid for iOS?

This is also valid for iOS because what you'd like to achieve is reacting immediately to the snap event which is a problem on both iOS and Android if you handle these events from within JS and not native only. You will have to find a way to declare the desired behaviour, encode it and send it from JS to native. From there it should be executed without further communication via the bus to guarantee smooth ux.

@talkol @rotemmiz Any thoughts about this issue? Do you think such UI requirement could be supported by react-native-interactable? If so, what would be the best approach (for iOS first) ?

I would like to contribute but not sure how to tackle this.

@jensneuse What's your hack to remove overlay instantly? I am trying something similar where I need to remove the overlay when user swipes a card out. However, onSnap event is firing slow (1-2s delay) on iOS which makes the overlay modal background slow to dismiss.

@chefkefu I've described it above.

The issue I see here is that onSnap may be called with a big delay. Of course this is not intentional, onSnap show be called instantly.

We will investigate.

Any progress on this?

I need to set a prop to change another component after snap happens depending on the position that the interactable view is in. onSnap doesn't get called instantly and makes it seem very laggy.

This makes swipe action looks really really laggy

Same issue here, onAlert works instant, onSnap works with delay

Issue solved :)
Use id for your snapPoints, example:

snapPoints={[
  { y: 40,  id: 'top' },
  { y: Screen.height - 300, id: 'middle' },
  { y: Screen.height - 100, id: 'bottom' }
]}

After what use onDrag:

 onDrag={event => {
     // Your current position - alert.nativeEvent.targetSnapPointId
     console.tron.log(event.nativeEvent.targetSnapPointId);
 }}

You will have instant snapPoints current position.

If you're not dragging (e.g. if you use instance.snapTo()) but you still require the event to fire instantly, add alertAreas to your view and then make use of the onAlert event.

Was this page helpful?
0 / 5 - 0 ratings