Related
I am trying to implement a Switch component in react native but the switch different in the android platform. I did explore a lot but I didn't find any references finally i found but it is in type script and a class-based component can someone help me to convert in JSX and functional-based component?
import * as React from 'react';
import {
Animated,
Easing,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
interface Props {
onColor: string;
offColor: string;
label: string;
onToggle: () => void;
style: object;
isOn: boolean;
labelStyle: object;
}
interface DefaultProps {
onColor: string;
offColor: string;
label: string;
onToggle: () => void;
style: object;
isOn: boolean;
labelStyle: object;
}
export default class Toggle extends React.PureComponent<Props> {
animatedValue = new Animated.Value(0);
static defaultProps: DefaultProps = {
onColor: '#4cd137',
offColor: '#ecf0f1',
label: '',
onToggle: () => {},
style: {},
isOn: false,
labelStyle: {},
};
render() {
const moveToggle = this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 20],
});
const {
isOn,
onColor,
offColor,
style,
onToggle,
labelStyle,
label,
} = this.props;
const color = isOn ? onColor : offColor;
this.animatedValue.setValue(isOn ? 0 : 1);
Animated.timing(this.animatedValue, {
toValue: isOn ? 1 : 0,
duration: 300,
easing: Easing.linear,
}).start();
return (
<View style={styles.container}>
{!!label && <Text style={[styles.label, labelStyle]}>{label}</Text>}
<TouchableOpacity
onPress={() => {
typeof onToggle === 'function' && onToggle();
}}>
<View
style={[styles.toggleContainer, style, { backgroundColor: color }]}>
<Animated.View
style={[
styles.toggleWheelStyle,
{
marginLeft: moveToggle,
},
]}
/>
</View>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
},
toggleContainer: {
width: 50,
height: 30,
marginLeft: 3,
borderRadius: 15,
justifyContent: 'center',
},
label: {
marginRight: 2,
},
toggleWheelStyle: {
width: 25,
height: 25,
backgroundColor: 'white',
borderRadius: 12.5,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.2,
shadowRadius: 2.5,
elevation: 1.5,
},
});
Basically, as a functional component in jsx, the above would translate to:
import * as React from 'react';
import {
Animated,
Easing,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import PropTypes from 'prop-types';
const Toggle = props => {
const animatedValue = new Animated.Value(0);
const moveToggle = animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 20],
});
const {isOn, onColor, offColor, style, onToggle, labelStyle, label} = props;
const color = isOn ? onColor : offColor;
animatedValue.setValue(isOn ? 0 : 1);
Animated.timing(animatedValue, {
toValue: isOn ? 1 : 0,
duration: 300,
easing: Easing.linear,
useNativeDriver: false,
}).start();
return (
<View style={styles.container}>
{!!label && <Text style={[styles.label, labelStyle]}>{label}</Text>}
<TouchableOpacity onPress={typeof onToggle === 'function' && onToggle}>
<View style={[styles.toggleContainer, style, {backgroundColor: color}]}>
<Animated.View
style={[
styles.toggleWheelStyle,
{
marginLeft: moveToggle,
},
]}
/>
</View>
</TouchableOpacity>
</View>
);
};
Toggle.propTypes = {
onColor: PropTypes.string,
offColor: PropTypes.string,
label: PropTypes.string,
onToggle: PropTypes.func,
style: PropTypes.object,
isOn: PropTypes.bool.isRequired,
labelStyle: PropTypes.object,
};
Toggle.defaultProps = {
onColor: '#4cd137',
offColor: '#ecf0f1',
label: '',
onToggle: () => {},
style: {},
isOn: false,
labelStyle: {},
};
export default Toggle;
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
},
toggleContainer: {
width: 50,
height: 30,
marginLeft: 3,
borderRadius: 15,
justifyContent: 'center',
},
label: {
marginRight: 2,
},
toggleWheelStyle: {
width: 25,
height: 25,
backgroundColor: 'white',
borderRadius: 12.5,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.2,
shadowRadius: 2.5,
elevation: 1.5,
},
});
USAGE:
import React, {useState} from 'react';
import {View} from 'react-native';
import Toggle from '..path/to/toggle';
const Screen = () => {
const [toggleIsOn, setToggle] = useState(false);
return (
<View>
<Toggle
isOn={toggleIsOn}
onToggle={() => {
setToggle(!toggleIsOn);
}}
/>
</View>
);
};
export default Screen;
You would need to install prop-types (yarn install prop-types) so you can specify types or use flow because js out of the box isn't type safe.
Switch button with typescript
import React, {useEffect, useState} from 'react';
import {Animated, Easing, StyleSheet, Text, TouchableOpacity, View} from
'react-native';
import theme from '#/constants/themeConstants';
type TProps = {
value: boolean;
onChange: () => void;
onColor?: string;
offColor?: string;
label?: string;
labelStyle?: any;
};
const Switch: React.FC<TProps> = ({
value,
onChange,
onColor = 'green',
offColor = 'grey',
label = '',
labelStyle,
}) => {
const [isEnabled, setIsEnabled] = useState(false);
useEffect(() => {
value && setIsEnabled(value);
}, [value]);
const toggleSwitch = () => {
setIsEnabled(!isEnabled);
onChange();
};
const animatedValue = new Animated.Value(0);
const moveToggle = animatedValue.interpolate({
inputRange: [-0.2, 0.9],
outputRange: [0, 20],
});
const color = value ? onColor : offColor;
animatedValue.setValue(value ? 0 : 1);
Animated.timing(animatedValue, {
toValue: value ? 1 : 0,
duration: 300,
easing: Easing.linear,
useNativeDriver: false,
}).start();
return (
<View style={styles.container}>
{!!label && <Text style={[styles.label, labelStyle]}>{label}
</Text>}
<TouchableOpacity onPress={toggleSwitch} activeOpacity={1}>
<View style={[styles.toggleContainer, {backgroundColor:
color}]}>
<Animated.View style={[styles.toggleWheelStyle,
{marginLeft: moveToggle}]} />
</View>
</TouchableOpacity>
</View>
);
};
export default Switch;
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
},
toggleContainer: {
width: 50,
height: 30,
marginLeft: 3,
borderRadius: 15,
justifyContent: 'center',
},
label: {
marginRight: 2,
},
toggleWheelStyle: {
width: 25,
height: 25,
backgroundColor: 'white',
borderRadius: 12.5,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.2,
shadowRadius: 2.5,
elevation: 1.5,
},
});
How I make my box to center like justify content center but with position absolute ?
top: '50%' is too close to the bottom its not centered.
Modal.tsx
import React, { useEffect } from 'react';
import { StyleSheet, View, Pressable, StyleProp, ViewStyle } from 'react-native';
import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
import Animated, { runOnJS, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
interface IModalCenter {
children: React.ReactNode;
onPress: () => void;
style?: StyleProp<ViewStyle>;
}
const ModalCenter = ({ children, onPress, style }: IModalCenter) => {
const x = useSharedValue(0);
useEffect(() => {
x.value = 1;
}, []);
const aView = useAnimatedStyle(() => {
const scale = withSpring(x.value);
return {
transform: [{ scale }]
}
});
return (
<GestureHandlerRootView style={s.container}>
<Animated.View style={{flexGrow: 1, backgroundColor: 'rgba(0,0,0,0.4)',}} />
<Animated.View style={[style, aView]}>
{ children }
</Animated.View>
</GestureHandlerRootView>
)
};
const s = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
flex: 1,
zIndex: 600,
margin: 'auto'
}
})
export default ModalCenter;
app.tsx
<ModalCenter style={{backgroundColor: '#fff', position: 'absolute', margin: 'auto', alignSelf: 'center', height: 200, width: 300, borderRadius: 8, elevation: 4}} onPress={handleToggleModalMessage}>
<Text>Hello</Text>
</ModalCenter>
how can i make it center ? ........................................................................................................................................................................................
You need to add 'justify-content' property to the container:
container: {
...StyleSheet.absoluteFillObject,
flex: 1,
zIndex: 600,
margin: 'auto',
justify-content: 'center' <---- add this
}
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]});
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.
I am trying to implement a Drag'n Drop functionality in my React Native application. I have already made a pretty good study of Gesture Responder System and PanResponder in particular as well as an Animated class, but I can not come up with a proper solution around connecting them with FlatList component.
The problem is that when I am trying to shift a Responder item beyond the scope of the View component wrapping FlatList (height: 80) it is visually getting hidden and parent Component obviously overlaps it..
Here is the screenShot: http://joxi.ru/82Q0dn1CwwE1Vm
Here is my code:
import React from 'react';
import {
SafeAreaView,
View,
FlatList,
Text,
StyleSheet,
Animated,
PanResponder
} from 'react-native';
import { v4 } from 'uuid';
export default class App extends React.PureComponent{
state = {
data: [
{ id: v4(), title: 'Lightcoral', hex: '#eb7474' },
{ id: v4(), title: 'Orchid', hex: '#eb74dc' },
{ id: v4(), title: 'Mediumpurple', hex: '#9a74eb' },
{ id: v4(), title: 'Mediumslateblue', hex: '#8274eb' },
{ id: v4(), title: 'Skyblue', hex: '#74b6eb' },
{ id: v4(), title: 'Paleturquoise', hex: '#93ece2' },
{ id: v4(), title: 'Palegreen', hex: '#93ecb6' },
{ id: v4(), title: 'Khaki', hex: '#d3ec93' }
]
}
_position = new Animated.ValueXY()
_panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: (event, gesture) => {
this._position.setValue({ x: gesture.dx, y: gesture.dy})
},
onPanResponderRelease: () => {}
})
_keyExtractor = (item) => item.id;
_renderItem = ({item}) => {
return (
<Animated.View
style={this._position.getLayout()}
{...this._panResponder.panHandlers}
>
<View style={[styles.itemBox, {backgroundColor: `${item.hex}`}]}>
<Text>{item.title}</Text>
</View>
</Animated.View>
)
}
render() {
const { data } = this.state
return (
<SafeAreaView style={styles.safeArea}>
<View style={styles.container}>
<View style={styles.targetArea}>
<Text>Drop HERE!!!</Text>
</View>
<View style={{ height: 80, borderColor: 'black', borderWidth: 2 }}>
<FlatList
data={data}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
horizontal={true}
/>
</View>
</View>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
safeArea: {
flex: 1
},
container: {
flex: 1,
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: '#fff',
},
itemBox: {
width: 80,
height: 80,
alignItems: 'center',
justifyContent: 'center',
borderWidth: 1,
borderColor: '#fff'
},
targetArea: {
height: 150,
width: 150,
alignItems: 'center',
justifyContent: 'center',
borderWidth: 1,
borderColor: '#eee',
backgroundColor: '#F5FCFF',
marginTop: 40
}
});
How can I change the location of the particular item of the FlatList, which is in a horizontal mode and has a fixed height, and move it to another area of the screen??
Please help me to knock that one out, every advice is much appreciated!