Drag up a ScrollView then continue scroll in React Native

淺唱寂寞╮ 提交于 2019-12-02 20:29:16

I've tried several options. This is my solution which works best performance-wise. Of course this is just a working example, you might need to optimize it a little bit further to perfectly match your needs.

Demo:

Explanation:

The basic idea is to keep the map all the time in the background of the overlay. The overlay is absolutely positioned and contains two Views. One View is transparent, where we can still see and control the map, the other View contains the actual ScrollView. The trick is to set pointerEvents="box-none" of the parent overlay and disable pointerEvents with pointerEvents="none" of View where we want to interact with the map.

Basic render method:

  <SafeAreaView style={{flex: 1}}>
     <MapView
      initialRegion={region}
      style={{height: HEIGHT, width: WIDTH}}
      /> 
    <View  pointerEvents="box-none"style={{height: HEIGHT, width: WIDTH, position: 'absolute'}}>
      <View pointerEvents="none" style={{height: this.state.height, backgroundColor: 'transparent'}} />
      <View style={{ height: HEIGHT-this.state.height, backgroundColor: 'white'}}>
        <ScrollView onScroll={(e) => this._onScroll(e)} scrollEventThrottle={10} >
        -- content of scrollview goes here --- 
        </ScrollView>
      </View>
    </View>

  </SafeAreaView>

Scrolling functionality:

If we scroll down the ScrollView we want to shrink the empty View, so that the ScrollView becomes fullscreen. Therefore we listen to the onScroll method of the ScrollView. See code and comments below:

  _onScroll(e){
    // from the nativeEvent we can get the contentOffsett
    var offset_y = e.nativeEvent.contentOffset.y;
    if (offset_y > 0 ) {
     if (this.state.height>=0){
      // we are scrolling down the list, decrease height of the empty view
      this.setState({height: this.state.height-offset_y});
     }
    }
    if (offset_y <0){
      if (this.state.height <= this.state.mapHeight){
        // we are scrolling up the list, increase size of empty view/map view 
        this.setState({height: this.state.height-offset_y});
      }
    }
  }

With ScrollView's scrollEventThrottle prop we can control how often the _onScroll method should be called. (Here you probably have to fine tune)

Complete Code & Working Snack

https://snack.expo.io/@tim1717/rnmapwithoverlay

I think it is better to place both MapView and BottomView into one ScrollView.

It will save natural scrolling behaviour and prevent side effects like white stripes on scrolling down or top view disappearing on scrolling up. The main problem here is how to make "freeze" effect for the MapView.

It is nice to use parallax effect by transforming MapView inside Animated container relatively to a scroll event. I think Animated library with Animatable components will be good solution for problems like that:

   scroll = new Animated.Value(0)
   render() {
      return <Animated.ScrollView ...
          onScroll={Animated.event([{nativeEvent: {contentOffset: {y: this.scroll}}}], {useNativeDriver: true})}>

        <Animated.View 
              style={
                 { ...transform: 
                       [{translateY: Animated.multiply(this.scroll, 0.8)}]}}>
          <MapView />
        </Animated.View>
        <BottomView />
      </Animated.ScrollView>
    }

Actually if you don't want parallax effect you can replace Animated.multiply(this.scroll, 0.8) with just this.scroll

You can see full example here: Code example

Screen capture

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!