benevbright/react-navigation-collapsible

Use with a Webview

Opened this issue · 5 comments

Information

  • react-native version: 0.61.4
  • react-navigation version: ^5.0.9
  • react-navigation-collapsible version: ^5.4.0
  • Platform (iOS/Android): Android (Maybe iOS but couldn't test it)
  • react-native init or Expo: Expo 37

Detail

The library works fine with a FlatList, but I couldn't figure how to make it work with a WebView.
I tried to create an animated view and pass it props like we would do with a FlatList, but the header would not move.

const AnimatedWebView = Animated.createAnimatedComponent(WebView);

const WebViewScreen = ({ navigation }: ScreenProps) => {
  const {
    onScroll,
    containerPaddingTop,
    scrollIndicatorInsetTop,
  } = useCollapsibleStack();

  return (
    <AnimatedWebView
      ref={this.webviewRef}
      onScroll={onScroll}
      contentContainerStyle={{ paddingTop: containerPaddingTop }}
      scrollIndicatorInsets={{ top: scrollIndicatorInsetTop }}
      source={{ uri: 'https://docs.expo.io' }}
    />
  );
};

Am I doing something wrong or is Webview support not implemented?

Snack

You can try the current behavior in this snack (edited from the one in the Readme, web doesn't work for some reason)

Thank you for your time

Found a workaround, jittery when scrolling slowly but works.

Native driver does not work with the webview, so it must be disabled in the stack config

        {createCollapsibleStack(
          <Stack.Screen
            name="WithWebView"
            component={WebViewScreen}
            options={{
              headerStyle: {backgroundColor: 'green'},
              headerTintColor: 'white',
              title: 'Collapsible WebView',
            }}
          />, {useNativeDriver: false}
        )}

This will make the header collapse, but the webview will start under it. Setting the padding like in a FlatList will render it at the right place, but will have a blank space under the header when scrolling. Content insets only works in iOS, so my workaround is to set the marginTop to the header height, the marginBottom to the negative header height so the global webview size does not change. Then set the transform to follow translateY prop to move the webview while scrolling.

<AnimatedWebView
      onScroll={onScroll}
      style={{
        marginTop: containerPaddingTop,
        marginBottom: -containerPaddingTop,
        transform: [{ translateY: translateY }],
      }}
      source={{ uri: 'https://docs.expo.io' }}
    />

You can edit the previous snack to see the effect.

@Keplyx Thanks a lot for your discovery.

One question. You don't see this bug on Android RN? facebook/react-native#21801
RN Android has this bug for a long time when you use translateY on ScrollView/FlatList. I'm curious if it occurs on Webview as well.

@benevbright Didn't know about this Android RN bug.

I edited your library to return the current header height:

const currentHeight = Animated.add(headerHeight, translateY);

and set this value as marginTop to try and not use translateY, but it didn't change much, still laggy.

Found a workaround that does not involve editing the library and does not have any stutter! The trick is not to change the website container style to match the header position, but to change the actual website styling.

I simply injected javascript in the webview to add a margin to the body, matching the header size. I also added a transition time to smooth out the experience because the javascript is loaded after page render.

The downsides (because sadly this is not a fix...) are that the page content will take time to get the right position and that it may break some websites.

    const customJS = "document.getElementsByTagName('body')[0].style.marginTop = '" + containerPaddingTop +"px';"
        + "document.getElementsByTagName('body')[0].style.transition = '50ms';true;";
    return (
        <AnimatedWebView
            onScroll={onScroll}
            injectedJavaScript={customJS}
            javaScriptEnabled={true}
            source={{uri: 'https://docs.expo.io/'}}
        />
    );

@Keplyx
Cool...!
I liked to suggest such a thing like that but I wasn't sure how to code that.
Thanks again.