React Native slide out panel and scrollview - javascript

I am developing an app with react native. I have this UI element which is similar to that of Maps in iOS, in which you slide a panel from the bottom and inside it, there is a scrollable list.
For the slide-out panel, I am using a component called rn-sliding-up-panel. It has several props as event listeners. For example
<SlidingUpPanel
allowDragging={/*Boolean*/}
onDragStart={()=>{} /*When it is about to be dragged*/}
onDrag={()=>{} /*When it is being dragged*/}
onDragEnd={()={} /*When the user is no longer touching the screen*/}
></SlidingUpPanel>
Inside it, I have a <ScrollView> containing a <List> from react-native-elements. As far as I know, it has only one vent listener, being:
<ScrollView onScroll={()=>{}}></ScrollView>
My issue is that scrolling on the list actually causes the panel to close (it closes by sliding down). I found a work-around by adding a state, and modfiying it onScroll:
state = {
dragPanel: true,
}
/*===========================================*/
<SlidingUpPanel allowDragging={this.state.dragPanel}>
<ScrollView onScroll={()={ this.setState({dragPanel: false}) }}></ScrollView>
</SlidingUpPanel>
However, I cannot find a way to restore the dragging, and it doesn't fire up as efficiently.
TL;DR
Is there an eficient way to implement a ScrollView inside a SlidingUpPanel without the events of each overlapping? Maybe using something similar to function(e){e.preventDefault();}?

To properly disable / restore outer scroll dragging, do
_onGrant() {
this.setState({ dragPanel: false });
return true;
}
_onRelease() {
this.setState({ dragPanel: true });
}
constructor(props) {
super(props);
this._onGrant = this._onGrant.bind(this);
this._onRelease = this._onRelease.bind(this);
this._panResponder = PanResponder.create({
onMoveShouldSetPanResponder: this._onGrant,
onPanResponderRelease: this._onRelease,
onPanResponderTerminate: this._onRelease,
});
}
render() {
<SlidingUpPanel allowDragging={this.state.dragPanel}>
<ScrollView
{...this._panResponder.panHandlers}
/>
</SlidingUpPanel>
}
From what I had been searching for a long time, preventDefault() is a pure web-javascript thing, I think there are no preventDefault in react-native.
From document section Handling Touches, react-native just use javascript to simulate Objc (iOS) & Java (Android) events.

Set the minimumDistanceThreshold property to something around 50. Maybe 30 for small screens and 50-60 for bigger ones. Do it like so:
<SlidingUpPanel minimumDistanceThreshold={isSmallScreen ? 30 : 50}>
<ScrollView style={{flex: 1}}>
</ScrollView>
</SlidingUpPanel>

It might be late for an answer but use your scroll view as absolute positioned and position it accordingly.
Think of the scroll view as a pop-up dialog that appears in front of the backdrop behind it. Upon clicking the backdrop, the pop-up dismisses. Apply similar logic to the issue by letting scroll view in front of the slide up panel.

Related

How to keep div always visible in mobile with user scalable page?

Basically I want to build a page that consists of a weekly calendar. Since the calendar is big I want users to be able to zoom out on it to have an overview and to scroll to the specific hour they want. The problem is that when scrolling the users lose the context of the day they are viewing. Its also important to note that the day is on the left of the page and the scroll is done horizontally.
I built a solution based on window.scrollX that uses a transform to translate the div to the left side of the viewport. This works well in a browser, but unfortunately it doesnt work on mobile. The only way for the translation to be correct is by unzooming the page to maximum on Chrome mobile at least. After trying to debug on Google Chrome responsive design, I see that window.scrollX event holds the value 0 even if the scroll bar is not at the beggining
I would like to know if someone had this problem and have any idea how i should keep a div visible always independently of the zoom and scroll. Note that adding the meta viewport minimum-scale=1 solves the problem but that way I cant unzoom no more.
EDIT:
Here's more info of what I'm doing currently. I'm using react and I have a hook (https://github.com/neo/react-use-scroll-position) that does the following:
useEffect(() => {
let requestRunning = null
function handleScroll () {
if (isBrowser && requestRunning === null) {
requestRunning = window.requestAnimationFrame(() => {
setScrollPosition(getScrollPosition())
requestRunning = null
})
}
}
if (isBrowser) {
window.addEventListener('scroll', handleScroll)
window.addEventListener('touchmove', handleScroll)
return () => {
window.removeEventListener('scroll', handleScroll)
window.removeEventListener('touchmove', handleScroll)
}
}
}, [])
I have changed the getScrollPosition() function to use document.body.getBoundingClientRect() without success, and the original function uses window.scrollX.
Here's a picture of what I want to achieve in mobile:
I get that to work, but by limiting the viewport to disable unzoom. But I want the user to be able to unzoom and scroll horizontally while that div is always on the left side of the visible window. Any ideas/alternatives would be really appreciated!

Debounce jsx element's rendering in React

I am wondering if it is possible to debounce a jsx element's rendering. I have an animation of a panel expanding that has some content in it. If the panel is empty (just the background) the animation is smooth and works as expected. The animation and associated components are all from Material-UI. The issue arises when the content is in the panel already so the animation (width expansion) just skips out to the width of the content making the animation look choppy. Here is a similar example to what I am referring to. This code is my code I am using in my panel and they work the same in terms of the expansion. Only difference is the content in this example is just lorem ipsum so the animation appears to work fine. Is it possible to debounce the <CardContent /> component's rendering like this?
{ open &&
(
_.debounce(e => {
return (
<CardContent>
{/*content in here*/}
</CardContent>
)
}, 300)
)
}
or something similar (this doesn't work) so the panel will be fully expanded through the animation before the content is rendered?
I'd like to add a comment however my reputation isn't above 50 yet so unfortunately I can't :p.
JS Animation
To answer your question, if you are using JavaScript animations like GSAP or AnimeJS you can have a state flag called. isAnimating = null. Then when your animation starts set the flag to true and when it's finished set it to false. Then in your render() method write a conditional render like this.state.isAnimating === false && <CardContent />.
CSS Animation
If you are using CSS animation you can use the transitionend event to wait for the animation to end. Therefore you have to create a ref on the animating DOM element to which you can attach the event.
Does that help you a bit? Please let me know if you have any other questions.
Here is a solution using react spring library
I added a state variable in order to display or not the card content
const [contentVisible, setContentVisible] = React.useState(false);
I created a spring to handle the width transition, I set contentVisible to true as soon as the width approaches the 300
const { width } = useSpring({
width: open ? 300 : 120,
onFrame: ({ width }) => setContentVisible(width > 299.8)
});
Finally, I created an animated card component
const AnimatedCard = animated(Card);
...
<AnimatedCard classes={{ root: classes.card }} style={{ width }}>

How to start react-native onTouchDown anination

I am using Expo and the latest version of React-Native and I want to provide a subtle tactile feedback for Components like a View or Button.
I currently use animatable to start a pulse animation on the onPress() event but the animation only fires once the finger is released.
I want a subtle size reduction whilst press then a smooth tween back when released - that would feel elegant and not annoying to me.
Can this be done? I thought Animation or Animatable would have easily supported this but I can’t find any similar examples.
You could make your own touchable using the Gesture Responder System
Basically you'll use the props onStartShouldSetResponder, onResponderGrant, and onResponderRelease passed to an Animated.View.
class MyTouchable extends React.PureComponent {
render(){
<Animated.View
style={{ your styles, and animation values }}
onStartShouldSetResponder={() => true}
onResponderGrant={({ nativeEvent }) => {
//run your animations here
}}
onResponderRelease={({ nativeEvent }) => {
//animate back to zero, call your onPress function
//passed via props
}}
/>
}
}

How to stop swipe in ScrollView?

look this video of current scrolling behavior. i want, as soon as finger is lifted from screen, to disable scrolling.
Edit: the video shows a user scrolling through a list. When the finger is lifted from the screen, you see the scrolling continues up or down for a short time.
I think what you need is:
<ScrollView
bounces={false}
/>
This way when you scroll finished, the scroll animation will not continue and immediately stopped.
What I believe you want is basically to stop the scroll when momentum beings. For that, you can use the onMomentumScrollBegin callback on your ScrollView and call .scrollToEnd({animated: false}) on it.
Here's how that would look:
<ScrollView
ref={(ref) => this.scrollView = ref}
scrollEnabled={this.state.scroll}
onMomentumScrollBegin={
() => this.scrollView.scrollToEnd({animated: false})
}
>
...CONTENT HERE...
</ScrollView>
i think u mean stop scrolling once user lifts his finger
try :
<ScrollView
...
decelerationRate = {0}
/>
check docs

OpenLayers: How can I redraw a map after a sidebar is collapsed?

I am using OpenLayers to display a map, and AdminLte for the interface.
My problem : When collapsing the main left sidebar on a given page, all the boxes and what it contains changes size (I don't really know how) so the maps gets bigger as well. The problem is that when it happens, all the features displayed on the maps apparently change position and are not where they are supposed to be anymore.
What I would like: To redraw the map after the sidebar collapses.
Any suggestion?
I tried:
$('.navbar-collapse').on('shown.bs.collapse', function() {
map.updateSize();
});
and:
$('.sidebar').on('shown.bs.collapse', function() {
map.updateSize();
});
but to no avail...
EDIT : My question is similar to this one: OpenLayers: How to re-align mouse coordinates and vector layers after fluid css rendering of map div but his solution doesn't work for me :)
EDIT 2 : Just to clarify: I think the solution to my problem would be to call the map.updateSize() method when the sidebar has finished collapsing. The problem is that I don't know how to catch the moment when the sidebar has finished collapsing/expanding!
A temporary solution I found was to start a timeout when the button triggering the sidebar collapse and then call the map.updateSize() method:
$('.sidebar-toggle').click(function(){
setTimeout(function(){ map.updateSize(); }, 500);
});
It works...but it's kind of meh :/
If you're trying to redraw the map after the sidebar collapses, change your event handler to the following:
$('.sidebar').on('hidden.bs.collapse', function() {
map.updateSize();
});
According to the list of event handlers here, the hidden.bs.collapse event is fired when a collapse element has been hidden from the user.
I have the same problem, using React and a class component my (awful) solution is this:
shouldComponentUpdate = async () =>
await new Promise(res =>
setTimeout(() => {
this.map.updateSize()
res(false)
}, 500)
)
It's awful because the resize causes the map to jump. If there is a way of achieving the map resize without a jump that would be pretty cool.
(500 is the animation time for my drawer to close)

Categories