Scrollable within PanController
Opened this issue · 10 comments
Thanks for making this little module!
I'm trying to implement some functionality but can't seem to get it to work correctly. What I essentially want is a scrollable pane which can be enlarged. When first opened, it would be 50% of the viewport height, but you can drag it upward and it will reach 100% of the viewport height. Once at 100% viewport height (ie. it is the entire viewport), you could scroll the contents. You could downsize it back to being 50% of the viewport by scrolling back to the top and dragging down.
Does that make sense? Is it possible to do this using PanController?
My trouble comes with the interaction between the ScrollView and the PanController—the ScrollView never takes over, so you can't scroll its contents.
Hi @morgante
Thanks for your question... I still haven't really "released" this component yet, so there are still some things that that I have left to implement and might not work correctly...
With that out of the way, let me see if I can understand what you are trying to do...
Using a ScrollView inside the PanController will likely never be possible. The PanController is meant to emulate things like the ScrollView (and additional behavior like you are wanting).
If I understand what you are trying to do, this should be pretty easy. Is there an app that already does this where you are trying to emulate the behavior? If you can post a gif of the UI, I can try and emulate it for you. If not, here is what I would recommend doing:
- Use the PanController to emulate the scrollview entirely. This can be done by simple enabling the vertical option and passing in an
Animated.Value
to the panY. - Create a
translateY
interpolated value from yourpanY
value like so:
var translateY = this.state.panY.interpolate({
inputRange: [0, breakpoint, breakpoint+1],
outputRange: [0,0,1]
})
- Apply this value to the
translateY
of the inner view - Create a
height
interpolated value from yourpanY
value like so:
var height = this.state.panY.interpolate({
inputRange: [0, breakpoint, breakpoint+1],
outputRange: [starting_height, screen_height, screen_height]
})
- Apply the
height
value to the height of the inner view. - You'll probably also have to tweak some things like the height of the container using the
measure
API. This is one of the things I have left to abstract out into the PanController component, but for now you'll have to do this manually.
Note: my ScrollView implementation would be a good starting point: https://github.com/lelandrichardson/react-native-pan-controller/blob/master/lib/ScrollView.js
Thanks for the pointers. I think that makes sense, but how do you handle the transition from scrolling within the pane to scrolling the pane down (decreasing its size).
The effect I'm trying to achieve can be seen in the Facebook Paper app. The cards at the bottom of the screen can be dragged left and right, or up, which increases their size until they take over the screen and become scrollable.
@morgante can you record a gif? The transition should be able to be handled through proper use of interpolation
@morgante is it something like this https://github.com/olivierlesnicki/react-native-switcher you're trying to achieve?
I've only just discovered react-native-pan-controller and I'm currently thinking about using it for the example above.
The more I think of it, the less I think it's possible. You'd need a way to compute an Animated value based on the values of the panX and panY (looks like a good example for Animated.Formula)
I'm actually trying to achieve the same thing:
var React = require('react-native');
var PanController = require('react-native-pan-controller').PanController;
var Switcher = React.createClass({
getDefaultProps() {
var scroll = new React.Animated.Value(0);
var zoom = new React.Animated.Value(0);
return {
scroll: scroll,
zoom: zoom,
};
},
getInitialState() {
return {
width: 0,
height: 0,
};
},
layout(e) {
this.setState({
width: e.nativeEvent.layout.width,
height: e.nativeEvent.layout.height,
});
},
render() {
var children = this.props.children.map ? this.props.children : [this.props.children];
return (
<React.View
onLayout={this.layout}
style={{
flex: 1,
}}
>
<PanController
momentumDecayConfig={{
decceleration: 0.1,
}}
horizontal={true}
vertical={true}
overshootX='spring'
overshootY='spring'
lockDirection={true}
snapSpacingX={this.state.width}
snapSpacingY={this.state.height / 6}
xBounds={[-this.state.width * (this.props.children.length - 1), 0]}
yBounds={[0, this.state.height / 3]}
panX={this.props.scroll}
panY={this.props.zoom}
style={{
flex: 1,
}}
>
{children.map((child, i) => {
var delta = this.props.scroll.interpolate({
inputRange: [-this.state.width * (i + 1), -this.state.width * i],
outputRange: [-1, 0],
});
var translateX = delta.interpolate({
inputRange: [-1, 0, 0.5, 1],
outputRange: [-this.state.width / 8, 0, this.state.width / 4, 2 * this.state.width / 3],
});
var translateY = this.props.zoom;
var scale = delta.interpolate({
inputRange: [0, 1],
outputRange: [0.73, 0.75],
});
return (
<React.Animated.View
key={i}
style={[{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
transform: [
{translateX},
{translateY},
{scale},
],
},]}>
{child}
</React.Animated.View>
);
})}
</PanController>
</React.View>
);
}
});
module.exports = Switcher;
I'd like to add the ability to zoom-in (instead of panY) the card and expand them full-width. Once they reach full-width, I want them to scroll without overlapping (like a normal scrollview).
@olivierlesnicki Yup, that's pretty much the same effect I was trying to go for.
I ended up writing the code myself directly using React Native.
@morgante Can you show me what you did please ? I'm stuck with the same issue.
@morgante @bulby97 @olivierlesnicki I'm going to start working on this project again, so I might be able to help you...
That react-native-switcher is pretty slick. i think this can be accomplished using the PanController.
Within the next week I hope to release some updates to this module which will include Android compatibility, and several working examples. I will also give a go at including a "Switcher" component like the react-native-switcher.
Hopefully with that example you may be able to extrapolate to your own needs?
Good job! Look forward to it @lelandrichardson
Nice work @lelandrichardson. Any progress regarding Android support?