问题
What I want (GIF)
Here is a (quality-reduced) GIF about what I'm want to achieve.
What I want (Text)
I have a scrollview
, that is positioned at the half of my screen.
What I want is to drag that scrollview
up, and then, when it reaches a certain position, send the drag touch event
to scrollview
itself, so it can continue scrolling.
My tries
- Put the scrollview in fullscreen in the foreground, and add a half-screen padding-top to its
contentContainerStyle
. It worked well, but I couldn't click the view behind it. Is there a way to click through the empty area of a scrollview? I also tried to detect the scroll position, in order to move the view up accordingly
<ScrollView onScroll={event => moveScrollViewUpIfNeeded(event)}> ... </ScrollView>
but didn't work when I tested it out on iOS simulator. Event is not even fired. React Native Docs states that
onScroll
needsscrollEventThrottle
to work. However,scrollEventThrottle
is only available on iOS. And in my case, I want it on Android too.
And If I successfully achieve this, I should face another UI problem: When dragging the ScrollView up, how can I prevent it to scroll when the view is not yet at the wanted position?
So, can you give me some tips to achieve this please:)?
Thank you,
回答1:
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
回答2:
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
来源:https://stackoverflow.com/questions/53523595/drag-up-a-scrollview-then-continue-scroll-in-react-native