Make React Native Modal appear from top to bottom - react-native

I noticed that the Modal component's animationType property only allows for it to slide from bottom to top. How could I change the animation and make the modal appear from top to bottom?
Thanks for your time.

It doesn't look like that component allows for that type of configuration.
One thing you could do is use the Animated library to create your own modal. You would set the translateY property to negative of of the height of the device, then animate the translateY value to 0:
openModal() {
Animated.timing(this.state.modalY, {
duration: 300,
toValue: 0
}).start();
},
closeModal() {
Animated.timing(this.state.modalY, {
duration: 300,
toValue: -deviceHeight
}).start();
},
A full implementation is below:
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
TouchableHighlight,
Animated,
Dimensions
} = React;
let deviceHeight = Dimensions.get('window').height
var deviceWidth = Dimensions.get('window').width
var SampleApp = React.createClass({
openModal() {
Animated.timing(this.state.modalY, {
duration: 300,
toValue: 0
}).start();
},
closeModal() {
Animated.timing(this.state.modalY, {
duration: 300,
toValue: -deviceHeight
}).start();
},
getInitialState(){
return {
modalY: new Animated.Value(-deviceHeight)
}
},
render() {
var Modal = <Animated.View style={[ styles.modal, { transform: [ {translateY: this.state.modalY}] }]}>
<TouchableHighlight onPress={ this.closeModal } underlayColor="green" style={ styles.button }>
<Text style={ styles.buttonText }>Close Modal</Text>
</TouchableHighlight>
</Animated.View>
return (
<View style={styles.container}>
<TouchableHighlight onPress={ this.openModal } underlayColor="green" style={ styles.button }>
<Text style={ styles.buttonText }>Show Modal</Text>
</TouchableHighlight>
{ Modal }
</View>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center'
},
button: {
backgroundColor: 'green',
alignItems: 'center',
height: 60,
justifyContent: 'center',
},
buttonText: {
color: 'white'
},
modal: {
height: deviceHeight,
width: deviceWidth,
position: 'absolute',
top:0,
left:0,
backgroundColor: '#ededed',
justifyContent: 'center',
}
});
AppRegistry.registerComponent('SampleApp', () => SampleApp);

Use react-native-modal community package. It has built in this feature. Set animationIn={'slideInDown'}, animationInTiming={300} properties.

Related

Issue with React Native animations

I'm facing a problem with centring the text after the animation finishes as you can see in the video here https://www.youtube.com/watch?v=hhBGUp9_GAY&feature=youtu.be. I want to get both titles perfectly centered horizontally on all devices no matter the screen width. I'm using the Animated API. Any suggestions?
Here is my approach
import React, { useEffect } from "react";
import { View, StyleSheet, Animated, Text, Dimensions, AsyncStorage } from "react-native";
export default function Welcome({ navigation }) {
const width = Dimensions.get('screen').width
let position1 = new Animated.ValueXY(0, 0);
let position2 = new Animated.ValueXY(0, 0);
useEffect(() => {
Animated.timing(position1, {
toValue: { x: width / 4.5, y: 0 },
duration: 900
}).start();
Animated.timing(position2, {
toValue: { x: -width / 3, y: 0 },
duration: 900
}).start();
}, []);
_retrieveData = async () => {
try {
const token = await AsyncStorage.getItem('tokehhn');
if (token !== null) {
// We have data!!
setTimeout(() => navigation.navigate('Home'), 2000)
} else {
setTimeout(() => navigation.navigate('Auth'), 2000)
}
} catch (error) {
// Error retrieving data
}
};
useEffect(() => {
_retrieveData()
}, [])
return (
<View style={styles.container}>
<Animated.View style={position1.getLayout()}>
{/* <View style={styles.ball} /> */}
<Text style={{ position: 'relative', fontWeight: 'bold', fontSize: 24, color: '#5790f9' }}>Welcome to Glue</Text>
</Animated.View>
<Animated.View style={position2.getLayout()}>
{/* <View style={styles.ball} /> */}
<Text style={{ position: 'relative', right: -220, fontWeight: 'bold', fontSize: 21, color: '#5790f9' }}>Where everything happens</Text>
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center'
}
});
Thats how you do it:
let {width} = Dimensions.get('window')
export default function App() {
let animation = new Animated.Value(-width);
let translateX = animation.interpolate({inputRange:[-width,0],outputRange:[2*width,0]});
React.useEffect(()=>{
Animated.timing(animation,{toValue:0}).start();
},[])//eslint-ignore-line
return (
<View style={styles.container}>
<Animated.Text style={[styles.text,{transform:[{translateX:animation}]}]}>LOL</Animated.Text>
<Animated.Text style={[styles.text,{transform:[{translateX}]}]}>Longer LOLLLL</Animated.Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
text:{
textAlign:'center'
}
});
I have created snack as well
Make it a simple and clean interpolation.
The code looks always clean, and readable if we use Animated.Value in range of 0 - 1.
Full code:
import React, {useEffect} from 'react';
import {View, StyleSheet, Animated} from 'react-native';
const App = () => {
const animate = new Animated.Value(0);
const inputRange = [0, 1];
const translate1 = animate.interpolate({inputRange, outputRange: [-100, 0]});
const translate2 = animate.interpolate({inputRange, outputRange: [100, 0]});
useEffect(() => {
Animated.timing(animate, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}).start();
}, []);
return (
<View style={styles.container}>
<Animated.Text
style={[styles.text, {transform: [{translateX: translate1}]}]}>
First Text
</Animated.Text>
<Animated.Text
style={[styles.text, {transform: [{translateX: translate2}]}]}>
Second Text
</Animated.Text>
</View>
);
};
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 25,
},
});
Using that animated value, implement any other animations if needed.
For example, If you need to scale the text while moving:
const scale = animate.interpolate({inputRange, outputRange: [1, 1.5]});

React Native Panresponder issue after retouching and moving the image return back to initial position

I have one draggable car image if i dragging the car first time it works fine after second time re dragging the car image return back to the initial position. I want to drag an image smooth and draggable with finger touch. please help me
import React, { Component } from 'react';
import { StyleSheet, View, Text, PanResponder, Animated, Easing, Dimensions, Image, Button } from 'react-native';
import { ToastAndroid } from 'react-native';
export default class Viewport extends Component {
constructor(props) {
super(props);
this.state = {
disableCar: false,
dropZoneCar: null,
panCar: new Animated.ValueXY(),
};
this.carFunction();
}
carFunction = () => {
this.panResponderCar = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: Animated.event([null, {
dx: this.state.panCar.x,
dy: this.state.panCar.y
}]),
onPanResponderGrant: (evt, gestureState) => {
console.log(evt)
},
onPanResponderRelease: (e, gesture) => {
// console.log(e)
if (this.isDropZoneCar(gesture)) {
ToastAndroid.show('Correct', ToastAndroid.SHORT);
} else {
ToastAndroid.show('Wrong', ToastAndroid.SHORT);
}
}
});
}
isDropZoneCar(gesture) {
var dz = this.state.dropZoneCar;
return gesture.moveY > dz.y && gesture.moveY < dz.y + dz.height;
}
setDropZoneCar(event) {
this.setState({
dropZoneCar: event.nativeEvent.layout
});
}
setDropZoneBike(event) {
this.setState({
dropZoneBike: event.nativeEvent.layout
});
}
render() {
return (
<View style={styles.mainContainer}>
<View style={{ flexDirection: 'row', }}>
<View style={{ flex: 1 }}>
<View
onLayout={this.setDropZoneCar.bind(this)}
style={[styles.dropZone, { backgroundColor: this.state.disableCar ? 'green' : '#2c3e50' }]}>
<Text style={styles.text}>Drop a Car</Text>
</View>
<View
onLayout={this.setDropZoneBike.bind(this)}
style={[styles.dropZone, { backgroundColor: this.state.disableCar ? 'green' : '#2c3e50' }]}>
<Text style={styles.text}>Drop a Bike</Text>
</View>
</View>
<View style={{ flex: 1 }}>
{this.draggableCar()}
</View>
</View>
</View>
);
}
draggableCar() {
return (
<View style={styles.draggableContainer} >
<Animated.View
{...this.panResponderCar.panHandlers}
style={[this.state.panCar.getLayout()]}>
<Image
style={{ position: "absolute", width: 200, height: 100, right: 10, top: 300, }}
source={require('./assets/carr.png')}
/>
</Animated.View>
</View>
);
}
}
let CIRCLE_RADIUS = 36;
let Window = Dimensions.get('window');
let styles = StyleSheet.create({
mainContainer: {
flex: 1
},
dropZone: {
height: 100,
backgroundColor: '#2c3e50',
marginTop: 100
},
text: {
marginTop: 25,
marginLeft: 5,
marginRight: 5,
textAlign: 'center',
color: '#fff'
},
draggableContainer: {
position: 'absolute',
top: Window.height / 2 - CIRCLE_RADIUS,
left: Window.width / 2 - CIRCLE_RADIUS,
},
});
You are listening to the delta of the finger movement dx and dy, so whenever you touch again, your pan values drop to 0's. You should set an offset on your pan values every time you touch to fix this. Add this piece of code:
onPanResponderGrant: (e, gestureState) => {
this.state.panCar.setOffset({x: this.state.panCar.x._value, y: this.state.panCar.y._value});
this.state.panCar.setValue({x: 0, y: 0});
}
This will set the offset for your pan to current position, so it doesn't jump back after consequent touches. Hope this helps.

react native dynamic image slider transition opacity

I'm looking to implement in react native , slider with dynamic images with fading transition (play with opacity).
what I did is
changeBackgroundImageNew = ()=>{
const TIME_TRANSITION = 4500
let images_length = this.props.AppStore.barbershop_details.images.length
let index_image = 0;
setInterval(()=>{
if(this.state.index_image == images_length-1){
index_image = 0
}else{
index_image= index_image+1
}
this.setState({index_image})
},4000)
Animated.loop(
Animated.sequence([
Animated.delay(1500),
Animated.parallel([
Animated.timing(this.state.img_background_opacity, {
toValue: 0,
duration: TIME_TRANSITION
}),
Animated.timing(this.state.img_background_opacity2, {
toValue: 1,
duration: TIME_TRANSITION
})
]),
Animated.delay(1500),
Animated.parallel([
Animated.timing(this.state.img_background_opacity2, {
toValue: 0,
duration: TIME_TRANSITION
}),
Animated.timing(this.state.img_background_opacity, {
toValue: 1,
duration: TIME_TRANSITION
})
])
])
).start(() => {
});
}
the render code
{AppStore.barbershop_details && AppStore.barbershop_details.images.map((image,i)=>(
<AnimatedImageBackground
key={i}
resizeMode="cover"
resizeMethod="resize"
style={[
style.img_background,
{ opacity: i == this.state.index_image?this.state.img_background_opacity:this.state.img_background_opacity2 }
]}
source={{uri:image}}
/>
))}
I'm trying to change the images with automatically animated fading transition,
kind of this example, but I want it in react native
example for images transition
I have implemented react-native-snap-carousel
Code:
import React, { Component } from "react";
import Carousel, { ParallaxImage } from 'react-native-snap-carousel';
import { Dimensions, StyleSheet,View,Text } from 'react-native';
const { width: screenWidth } = Dimensions.get('window')
export default class App extends Component {
constructor() {
super();
this.state = {
entries: [
{
"ID": "001",
"Name": "001",
"thumbnail": "https://wallpapercave.com/wp/KaNO7Ya.jpg"
},
{
"ID": "002",
"Name": "002",
"thumbnail": "https://wallpapercave.com/wp/ZxV8qRo.jpg"
},
{
"ID": "003",
"Name": "003",
"thumbnail": "https://wallpapercave.com/wp/ytM5xOy.jpg"
}
,
{
"ID": "004",
"Name": "004",
"thumbnail": "https://wallpapercave.com/wp/STgHPst.jpg"
}
,
{
"ID": "005",
"Name": "005",
"thumbnail": "https://wallpapercave.com/wp/vg5q7pY.jpg"
}
,
{
"ID": "006",
"Name": "006",
"thumbnail": "https://wallpapercave.com/wp/b2HsGgL.jpg"
}
],
}
}
_renderItem ({item, index}, parallaxProps) {
return (
<View style={styles.item}>
<ParallaxImage
source={{ uri: item.thumbnail }}
containerStyle={styles.imageContainer}
style={styles.image}
parallaxFactor={0.4}
{...parallaxProps}
/>
<Text style={styles.title} numberOfLines={2}>
{ item.Name }
</Text>
</View>
);
}
render () {
return (
<View style={styles.container}>
<Carousel
sliderWidth={screenWidth}
sliderHeight={screenWidth}
itemWidth={screenWidth - 60}
data={this.state.entries}
renderItem={this._renderItem}
hasParallaxImages={true}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container:{
marginTop:50
},
item: {
width: screenWidth - 60,
height: screenWidth - 60,
},
imageContainer: {
flex: 1,
marginBottom: Platform.select({ ios: 0, android: 1 }), // Prevent a random Android rendering issue
backgroundColor: 'white',
borderRadius: 8,
},
image: {
...StyleSheet.absoluteFillObject,
resizeMode: 'cover',
},
})
Output:
https://gph.is/g/ZdoYdWw
You can play with this component and get your desired output rather than creating your own custom component. You can create your custom image container and also set fade duration, etc. For more details, go through with its documentation.
English is not my mother language; please excuse any errors on my part.
please try this code as per your example:
try with View tag style backgroundColor:'#5f9ea0',//change color and without Image set background for it.
imoprt { ScrollView, Dimensions} from 'react-native';
render(){
let screenWidth = Dimensions.get('window').width;
let screenHeight = Dimensions.get('window').height;
return(
<ScrollView
horizontal={true}
pagingEnabled= {true}
showHorizontalScrollIndicator={true}
>
<View style={{
flex:1,
marginTop:20,
width: screenWidth,
justifyContent: 'center',
alignItems: 'center'
}}>
<Image source={} style={{ flex: 1, resizeMode: 'cover', // or 'stretch'}} />
// add your UI code for create like
<Text> Screen 1</Text>
</View>
<View style={{
flex:1,
marginTop:20,
width: screenWidth,
justifyContent: 'center',
alignItems: 'center'
}}>
<Image source={} style={{ flex: 1, resizeMode: 'cover', // or 'stretch'}} />
// add your UI code for create like
<Text> Screen 2</Text>
</View>
<View style={{
flex:1,
marginTop:20,
width: screenWidth,
justifyContent: 'center',
alignItems: 'center'
}}>
<Image source={} style={{ flex: 1, resizeMode: 'cover', // or 'stretch'}} />
// add your UI code for create like
<Text> Screen 3</Text>
</View>
);
}
I hope it will help you. On fad Effect see the link: https://www.youtube.com/watch?v=K2B0vVIHV_A
try used this package react-native-image-slider-show hear example example for how to used it

How to Hide a component in react native

Hi I have TextInput like this
Now when i enter text inside TextInput and when i click on cancel then the it is clearing the text inside TextInput , Now along with this I want to hide Cancel when is is clicked.
Here is my code
import React, { Component } from 'react';
import {
Dimensions,
View,
Animated,
TextInput,
TouchableOpacity,
StyleSheet,
} from 'react-native';
import colors from '../styles/colors';
const { width } = Dimensions.get('window');
const PADDING = 16;
const SEARCH_FULL_WIDTH = width - PADDING * 2; //search_width when unfocused
const SEARCH_SHRINK_WIDTH = width - PADDING - 90; //search_width when focused
const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity);
export default class Test extends Component {
constructor(props) {
super(props);
this.state = {
inputLength: new Animated.Value(SEARCH_FULL_WIDTH),
cancelPosition: new Animated.Value(0),
opacity: new Animated.Value(0),
searchBarFocused: false,
text: '',
showCancel: true
};
}
onFocus = () => {
Animated.parallel([
Animated.timing(this.state.inputLength, {
toValue: SEARCH_SHRINK_WIDTH,
duration: 250
}),
Animated.timing(this.state.cancelPosition, {
toValue: 16,
duration: 400
}),
Animated.timing(this.state.opacity, {
toValue: 1,
duration: 250
}),
]).start();
};
onBlur = () => {
Animated.parallel([
Animated.timing(this.state.inputLength, {
toValue: SEARCH_FULL_WIDTH,
duration: 250
}),
Animated.timing(this.state.cancelPosition, {
toValue: 0,
duration: 250
}),
Animated.timing(this.state.opacity, {
toValue: 0,
duration: 250
})
]).start();
};
clearInput = () => {
this.textInputRef.clear();
//this.onFocus.bind(this);
this.setState({
showCancel: !this.state.showCancel
});
}
_renderCancel() {
if (this.state.showCancel) {
return (
<AnimatedTouchable
style={[styles.cancelSearch, { right: this.state.cancelPosition }]}
onPress={(this.clearInput.bind(this))}
>
<Animated.Text
style={[styles.cancelSearchText, { opacity: this.state.opacity, color: colors.darkBlue }]}
onChangeText={text => this.setState({ text })}
value={this.state.text}
>
Cancel
</Animated.Text>
</AnimatedTouchable>
);
} else {
return null;
}
}
render() {
const { searchBarFocused } = this.state;
return (
<View style={styles.searchContainer}>
<Animated.View
style={[
styles.search,
{
width: this.state.inputLength,
position: 'absolute',
left: 16,
alignSelf: 'center'
},
searchBarFocused === true ? undefined : { justifyContent: 'center' }
]}
>
<TextInput
style={styles.searchInput}
onBlur={this.onBlur}
onFocus={this.onFocus}
placeholder="Enter condition, primary care, speciality"
ref={(ref) => { this.textInputRef = ref; }}
/>
</Animated.View>
<AnimatedTouchable
style={[styles.cancelSearch, { right: this.state.cancelPosition }]}
onPress={this.clearInput.bind(this)}
>
<Animated.Text
style={[styles.cancelSearchText, { opacity: this.state.opacity, color: colors.darkBlue }]}
onChangeText={text => this.setState({ text })}
value={this.state.text}
>
Cancel
</Animated.Text>
</AnimatedTouchable>
{this._renderCancel() }
</View>
);
}
}
const styles = StyleSheet.create({
searchContainer: {
flexDirection: 'row',
height: 72,
borderColor: colors.light_green,
paddingTop: 100
},
search: {
height: 40,
width: '100%',
marginTop: 5,
borderRadius: 6,
alignItems: 'flex-start',
justifyContent: 'flex-start',
borderColor: colors.light_green,
fontSize: 20,
borderWidth: 1,
},
cancelSearch: {
position: 'absolute',
marginHorizontal: 16,
textAlign: 'center',
justifyContent: 'center',
alignSelf: 'center',
color: colors.darkBlue
}
});
can anyone help me how to hide Cancel when it is clicked.
when cancel is clicked i want my original TextInput to be like this
set the style to following :
style={{ opacity: this.state.isVisible ? 1 : 0 }}
and set state isVisibile accordingly to make it visible/hidden.
If you are trying to implement the clear text then you can use
clearButtonMode.
clearButtonMode
Else if you are using state i.e showCancel you can use as
{showCancel? <CancelButton/> : <View/>}

React Native animated input text

I want to show a cancel button, on the focus TextInput animation.
I did the following code, but a cancel button does not display and follow the box when focused. It's only shown after the animation end.
And when cancel button displayed, it is not on the same line with textinput.
How do I fix this?
const { width } = Dimensions.get('window');
const PADDING = 16;
const SEARCH_FULL_WIDTH = width - PADDING * 2; //search_width when unfocused
const SEARCH_SHRINK_WIDTH = width - PADDING - 90; //search_width when focused
class Search extends React.Component {
constructor(props: IProps) {
super(props);
this.state = {
inputLength: new Animated.Value(SEARCH_FULL_WIDTH),
searchBarFocused: false,
}
}
private onFocus = () => {
Animated.timing(this.state.inputLength, {
toValue: SEARCH_SHRINK_WIDTH,
duration: 250,
}).start(() => this.setState({ searchBarFocused: true }));
}
private onBlur = () => {
Animated.timing(this.state.inputLength, {
toValue: SEARCH_FULL_WIDTH,
duration: 250,
}).start(() => this.setState({ searchBarFocused: false }));
}
<View style={styles.searchContainer}>
<Animated.View style={[
styles.search,
{
width: this.state.inputLength,
position: 'absolute',
left: 16,
alignSelf: 'center'
},
searchBarFocused === true ? undefined : { justifyContent: 'center' }
]}>
<Image source={searchIcon} style={styles.image} />
<TextInput
style={styles.searchInput}
....
onBlur={this.onBlur}
onFocus={this.onFocus}
/>
</Animated.View>
{searchBarFocused &&
<Touchable style={styles.cancelSearch} onPress={this.cancelSearch}>
<Text style={styles.cancelSearchText}>Cancel</Text>
</Touchable>
}
</View>
const styles = StyleSheet.create({
searchContainer: {
flexDirection: 'row',
height: 72,
borderBottomColor: SOLITUDE_COLOR,
},
search: {
flex: 1,
flexDirection: 'row',
height: 40,
borderRadius: 6,
},
cancelSearch: {
marginHorizontal: 16,
textAlign: 'center',
justifyContent: 'center'
}
});
gif: when unfocus and focused
Here is a slightly modified version of your code.
import React from "react";
import {
Dimensions,
View,
Animated,
TextInput,
TouchableOpacity,
StyleSheet,
} from "react-native";
const { width } = Dimensions.get("window");
const PADDING = 16;
const SEARCH_FULL_WIDTH = width - PADDING * 2; //search_width when unfocused
const SEARCH_SHRINK_WIDTH = width - PADDING - 90; //search_width when focused
const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity);
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
inputLength: new Animated.Value(SEARCH_FULL_WIDTH),
cancelPosition: new Animated.Value(0),
opacity: new Animated.Value(0),
searchBarFocused: false
};
}
onFocus = () => {
Animated.parallel([
Animated.timing(this.state.inputLength, {
toValue: SEARCH_SHRINK_WIDTH,
duration: 250
}),
Animated.timing(this.state.cancelPosition, {
toValue: 16,
duration: 400
}),
Animated.timing(this.state.opacity, {
toValue: 1,
duration: 250
})
]).start();
};
onBlur = () => {
Animated.parallel([
Animated.timing(this.state.inputLength, {
toValue: SEARCH_FULL_WIDTH,
duration: 250
}),
Animated.timing(this.state.cancelPosition, {
toValue: 0,
duration: 250
}),
Animated.timing(this.state.opacity, {
toValue: 0,
duration: 250
})
]).start();
};
render() {
const { searchBarFocused } = this.state;
return (
<View style={styles.searchContainer}>
<Animated.View
style={[
styles.search,
{
width: this.state.inputLength,
position: "absolute",
left: 16,
alignSelf: "center"
},
searchBarFocused === true ? undefined : { justifyContent: "center" }
]}
>
<TextInput
style={styles.searchInput}
onBlur={this.onBlur}
onFocus={this.onFocus}
placeholder="Type something"
/>
</Animated.View>
<AnimatedTouchable
style={[styles.cancelSearch, { right: this.state.cancelPosition }]}
onPress={() => null}
>
<Animated.Text
style={[styles.cancelSearchText, { opacity: this.state.opacity }]}
>
Cancel
</Animated.Text>
</AnimatedTouchable>
</View>
);
}
}
const styles = StyleSheet.create({
searchContainer: {
flexDirection: "row",
height: 72,
borderBottomColor: "#00000033",
paddingTop: 100
},
search: {
flex: 1,
flexDirection: "row",
height: 40,
borderRadius: 6,
backgroundColor: "red"
},
cancelSearch: {
position: "absolute",
marginHorizontal: 16,
textAlign: "center",
justifyContent: "center",
alignSelf: "center"
}
});
You're setting searchBarFocused only after your animation completes. Since the cancel button is conditionally rendered based on searchBarFocused, it only appears at the end of the animation.