ComponentDidMount vs useEffect - javascript

There is a class components work piece. I want to convert it into a function component and implement it in the same way, but there's a problem.
Unlike the class type, the function type returns to the place where it was created without staying after the picture has moved.
This is the function components.
const [dogs, setDogs] = useState([])
function addDog(){
// setDogs([...dogs, {id: dogCount++}])
dogCount++;
console.log(dogCount)
// console.log(dogs)
const newDog = {id: dogCount, bottom: 125}
setDogs([...dogs, newDog])
}
return (
<View style={styles.entirebottomsheet}>
<View style={styles.container}>
<TouchableOpacity onPress={()=>{addDog();}} style={styles.dogButton}>
<Text>Dog</Text>
</TouchableOpacity>
{dogs.map(dog => {
return (
<DogContainer
key={dog.id}
style={{bottom: dog.bottom, position: 'absolute'}}
/>
);
})}
</View>
</View>
);
function DogContainer () {
const positionX = new Animated.Value(0);
const positionY = new Animated.Value(0);
// DogContainer.defaultProps = {
// onComplete() {}
// }
const getDogStyle = () => ({
transform: [{translateX: positionX}],
});
useEffect(() => {
Animated.spring(positionX, {
duration: 1000,
toValue: width / 3,
easing: Easing.ease,
useNativeDriver: true,
}).start();
Animated.spring(positionY, {
duration: 1000,
toValue: width / 3,
easing: Easing.ease,
useNativeDriver: true,
}).start();
return (
console.log('finish')
)
}, []);
return (
<Animated.View style={[{ bottom: 125},{
transform: [{translateX: positionX}, {translateY: positionY}],
}]}>
<Image source={dogImg} style={{width: 60, height: 60}}/>
</Animated.View>
);
}
This is the class components.
export default class App extends React.Component {
// let {up} = this.props.up;
state = {
dogs: [],
cats: [],
chicks: [],
};
addDog = () => {
console.log(...this.state.dogs);
this.setState(
{
dogs: [
...this.state.dogs,
{
id: dogCount,
bottom: 125,
},
],
},
() => {
dogCount++;
},
);
};
removeDog = id => {
this.setState({
dogs: this.state.dogs.filter(dog => {
return dog.id !== id;
}),
});
};
addCat = () => {
console.log(...this.state.cats);
this.setState(
{
cats: [
...this.state.cats,
{
id: catCount,
bottom: 125,
},
],
},
() => {
catCount++;
},
);
};
removeCat = id => {
this.setState({
cats: this.state.cats.filter(cat => {
return cat.id !== id;
}),
});
};
addChick = () => {
console.log(...this.state.chicks);
this.setState(
{
chicks: [
...this.state.chicks,
{
id: chickCount,
bottom: 125,
},
],
},
() => {
chickCount++;
},
);
};
removeChick = id => {
this.setState({
chicks: this.state.chicks.filter(chick => {
return chick.id !== id;
}),
});
};
render() {
return (
<GestureHandlerRootView>
<View style={styles.entirebottomsheet}>
<View style={styles.container}>
<TouchableOpacity onPress={this.addDog} style={styles.dogButton}>
<Text>Dog</Text>
</TouchableOpacity>
{this.state.dogs.map(dog => {
return (
<DogContainer
key={dog.id}
style={{bottom: dog.bottom, position: 'absolute'}}
onComplete={() => this.removeDog(dog.id)}
/>
);
})}
</View>
<View style={styles.container}>
<TouchableOpacity onPress={this.addCat} style={styles.catButton}>
<Text>Cat</Text>
</TouchableOpacity>
{this.state.cats.map(cat => {
return (
<CatContainer
key={cat.id}
style={{bottom: cat.bottom, position: 'absolute'}}
onComplete={() => this.removeCat(cat.id)}
/>
);
})}
</View>
<View style={styles.container}>
<TouchableOpacity onPress={this.addChick} style={styles.chickButton}>
<Text style={{position: 'absolute'}}>Chick</Text>
</TouchableOpacity>
{this.state.chicks.map(chick => {
return (
<ChickContainer
key={chick.id}
style={{bottom: chick.bottom, position: 'absolute'}}
onComplete={() => this.removeChick(chick.id)}
/>
);
})}
</View>
</View>
</GestureHandlerRootView>
);
}
}
class DogContainer extends React.Component {
state = {
position: new Animated.ValueXY({
x: 0,
y: 0,
}),
};
static defaultProps = {
onComplete() {},
};
componentDidMount() {
Animated.spring(this.state.position.x, {
duration: 1000,
toValue: width / 3,
easing: Easing.ease,
useNativeDriver: true,
}).start(this.props.onComplete);
Animated.spring(this.state.position.y, {
duration: 1000,
toValue: -340,
easing: Easing.ease,
useNativeDriver: true,
}).start(this.props.onComplete);
}
getDogStyle() {
return {
transform: [
{translateY: this.state.position.y},
{translateX: this.state.position.x},
],
};
}
render() {
return (
<Animated.View style={[this.getDogStyle(), this.props.style]}>
<Image source={dogImg} style={{width: 60, height: 60, borderRadius: 30}} />
</Animated.View>
);
}
}

Try to use useRef() hook
function DogContainer () {
const positionX = useRef(new Animated.Value(0)).current;
const positionY = useRef(new Animated.Value(0)).current;
...

In the Class-based version of DogContainer the position coordinate was a part of state.
class DogContainer extends React.Component {
state = {
position: new Animated.ValueXY({
x: 0,
y: 0,
}),
};
static defaultProps = {
onComplete() {},
};
componentDidMount() {
Animated.spring(this.state.position.x, {
duration: 1000,
toValue: width / 3,
easing: Easing.ease,
useNativeDriver: true,
}).start(this.props.onComplete);
Animated.spring(this.state.position.y, {
duration: 1000,
toValue: -340,
easing: Easing.ease,
useNativeDriver: true,
}).start(this.props.onComplete);
}
getDogStyle() {
return {
transform: [
{translateY: this.state.position.y},
{translateX: this.state.position.x},
],
};
}
render() {
return (
<Animated.View style={[this.getDogStyle(), this.props.style]}>
<Image source={dogImg} style={{width: 60, height: 60, borderRadius: 30}} />
</Animated.View>
);
}
}
Function component using the useState hook
function DogContainer ({ onComplete = () => {} }) {
const [position, setPosition] = React.useState(
new Animated.ValueXY({
x: 0,
y: 0,
})
);
const getDogStyle = () => ({
transform: [{
translateX: position.x,
translateY: position.y,
}],
});
useEffect(() => {
Animated.spring(position.x, {
duration: 1000,
toValue: width / 3,
easing: Easing.ease,
useNativeDriver: true,
}).start(onComplete);
Animated.spring(position.y, {
duration: 1000,
toValue: width / 3,
easing: Easing.ease,
useNativeDriver: true,
}).start(onComplete);
console.log('finish');
}, []);
return (
<Animated.View
style={[
{ bottom: 125},
{ transform: [
{ translateX: position.x },
{ translateY: position.y }
],
}
]}
>
<Image source={dogImg} style={{ width: 60, height: 60 }} />
</Animated.View>
);
}

Related

React Native Wheel of Fortune

I am developing the react native application which contains the wheel of fortune. There are two wheels in my application. Wheel#1 is running smoothly but in Wheel#2 is being required to add Inner & Outer wheels. I am using D3 shape and React Native SVG packages to achieve my goal.
import React, {Component} from 'react';
import {
View,
StyleSheet,
Dimensions,
Animated,
TouchableOpacity,
Image,
} from 'react-native';
import * as d3Shape from 'd3-shape';
import Svg, {G, Text, TSpan, Path, Pattern} from 'react-native-svg';
const AnimatedSvg = Animated.createAnimatedComponent(Svg);
const {width, height} = Dimensions.get('screen');
class WheelOfFortune extends Component {
constructor(props) {
super(props);
this.state = {
enabled: false,
started: false,
finished: false,
winner: null,
gameScreen: new Animated.Value(width - 40),
wheelOpacity: new Animated.Value(1),
imageLeft: new Animated.Value(width / 2 - 30),
imageTop: new Animated.Value(height / 2 - 70),
};
this.angle = 0;
this.prepareWheel();
}
prepareWheel = () => {
this.Rewards = this.props.options.rewards;
this.RewardCount = this.Rewards.length;
this.numberOfSegments = this.RewardCount;
this.fontSize = 20;
this.oneTurn = 360;
this.angleBySegment = this.oneTurn / this.numberOfSegments;
this.angleOffset = this.angleBySegment / 2;
this.winner = this.props.options.winner
? this.props.options.winner
: Math.floor(Math.random() * this.numberOfSegments);
this._wheelPaths = this.makeWheel();
this._angle = new Animated.Value(0);
this.props.options.onRef(this);
};
resetWheelState = () => {
this.setState({
enabled: false,
started: false,
finished: false,
winner: null,
gameScreen: new Animated.Value(width - 40),
wheelOpacity: new Animated.Value(1),
imageLeft: new Animated.Value(width / 2 - 30),
imageTop: new Animated.Value(height / 2 - 70),
});
};
_tryAgain = () => {
this.prepareWheel();
this.resetWheelState();
this.angleListener();
this._onPress();
};
angleListener = () => {
this._angle.addListener(event => {
if (this.state.enabled) {
this.setState({
enabled: false,
finished: false,
});
}
this.angle = event.value;
});
};
componentWillUnmount() {
this.props.options.onRef(undefined);
}
componentDidMount() {
this.angleListener();
}
makeWheel = () => {
const data = Array.from({length: this.numberOfSegments}).fill(1);
const arcs = d3Shape.pie()(data);
var colors = this.props.options.colors
? this.props.options.colors
: [
'#E07026',
'#E8C22E',
'#ABC937',
'#4F991D',
'#22AFD3',
'#5858D0',
'#7B48C8',
'#D843B9',
'#E23B80',
'#D82B2B',
];
return arcs.map((arc, index) => {
const instance = d3Shape
.arc()
.padAngle(0.01)
.outerRadius(width / 2)
.innerRadius(this.props.options.innerRadius || 100);
return {
path: instance(arc),
color: colors[index % colors.length],
value: this.Rewards[index],
centroid: instance.centroid(arc),
};
});
};
_getWinnerIndex = () => {
const deg = Math.abs(Math.round(this.angle % this.oneTurn));
// wheel turning counterclockwise
if (this.angle < 0) {
return Math.floor(deg / this.angleBySegment);
}
// wheel turning clockwise
return (
(this.numberOfSegments - Math.floor(deg / this.angleBySegment)) %
this.numberOfSegments
);
};
_onPress = () => {
const duration = this.props.options.duration || 10000;
this.setState({
started: true,
});
Animated.timing(this._angle, {
toValue:
365 -
this.winner * (this.oneTurn / this.numberOfSegments) +
360 * (duration / 1000),
duration: duration,
useNativeDriver: true,
}).start(() => {
const winnerIndex = this._getWinnerIndex();
this.setState({
finished: true,
winner: this._wheelPaths[winnerIndex].value,
});
this.props.getWinner(this._wheelPaths[winnerIndex].value, winnerIndex);
});
};
_textRender = (x, y, number, i) => (
<Text
x={x - number.length * 5}
y={y - 80}
fill={
this.props.options.textColor ? this.props.options.textColor : '#fff'
}
textAnchor="middle"
fontSize={this.fontSize}>
{Array.from({length: number.length}).map((_, j) => {
// Render reward text vertically
if (this.props.options.textAngle === 'vertical') {
return (
<TSpan x={x} dy={this.fontSize} key={`arc-${i}-slice-${j}`}>
{number.charAt(j)}
</TSpan>
);
}
// Render reward text horizontally
else {
return (
<TSpan
y={y - 40}
dx={this.fontSize * 0.07}
key={`arc-${i}-slice-${j}`}>
{number.charAt(j)}
</TSpan>
);
}
})}
</Text>
);
_renderSvgWheel = () => {
return (
<View style={styles.container}>
{this._renderKnob()}
<Animated.View
style={{
alignItems: 'center',
justifyContent: 'center',
transform: [
{
rotate: this._angle.interpolate({
inputRange: [-this.oneTurn, 0, this.oneTurn],
outputRange: [
`-${this.oneTurn}deg`,
`0deg`,
`${this.oneTurn}deg`,
],
}),
},
],
backgroundColor: this.props.options.backgroundColor
? this.props.options.backgroundColor
: '#fff',
width: width - 20,
height: width - 20,
borderRadius: (width - 20) / 2,
borderWidth: this.props.options.borderWidth
? this.props.options.borderWidth
: 2,
borderColor: this.props.options.borderColor
? this.props.options.borderColor
: '#fff',
opacity: this.state.wheelOpacity,
}}>
<AnimatedSvg
width={this.state.gameScreen}
height={this.state.gameScreen}
viewBox={`0 0 ${width} ${width}`}
style={{
transform: [{rotate: `-${this.angleOffset}deg`}],
margin: 10,
}}>
<G y={width / 2} x={width / 2}>
{this._wheelPaths.map((arc, i) => {
const [x, y] = arc.centroid;
const number = arc.value.toString();
return (
<G key={`arc-${i}`}>
<Path d={arc.path} strokeWidth={2} fill={arc.color} />
<G
rotation={
(i * this.oneTurn) / this.numberOfSegments +
this.angleOffset
}
origin={`${x}, ${y}`}>
{this._textRender(x, y, number, i)}
</G>
</G>
);
})}
</G>
</AnimatedSvg>
</Animated.View>
</View>
);
};
_renderKnob = () => {
const knobSize = this.props.options.knobSize
? this.props.options.knobSize
: 20;
// [0, this.numberOfSegments]
const YOLO = Animated.modulo(
Animated.divide(
Animated.modulo(
Animated.subtract(this._angle, this.angleOffset),
this.oneTurn,
),
new Animated.Value(this.angleBySegment),
),
1,
);
return (
<Animated.View
style={{
width: knobSize,
height: knobSize * 2,
justifyContent: 'flex-end',
zIndex: 1,
opacity: this.state.wheelOpacity,
transform: [
{
rotate: YOLO.interpolate({
inputRange: [-1, -0.5, -0.0001, 0.0001, 0.5, 1],
outputRange: [
'0deg',
'0deg',
'35deg',
'-35deg',
'0deg',
'0deg',
],
}),
},
],
}}>
<Svg
width={knobSize}
height={(knobSize * 100) / 57}
viewBox={`0 0 57 100`}
style={{
transform: [{translateY: 8}],
}}>
<Image
source={
this.props.options.knobSource
? this.props.options.knobSource
: require('../assets/images/knob.png')
}
style={{ width: knobSize, height: (knobSize * 100) / 57 }}
/>
</Svg>
</Animated.View>
);
};
_renderTopToPlay() {
if (this.state.started == false) {
return (
<TouchableOpacity onPress={() => this._onPress()}>
{this.props.options.playButton()}
</TouchableOpacity>
);
}
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity
style={{
position: 'absolute',
width: width,
height: height / 2,
justifyContent: 'center',
alignItems: 'center',
}}>
<Animated.View style={[styles.content, {padding: 10}]}>
{this._renderSvgWheel()}
</Animated.View>
</TouchableOpacity>
{this.props.options.playButton ? this._renderTopToPlay() : null}
</View>
);
}
}
export default WheelOfFortune;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
content: {},
startText: {
fontSize: 50,
color: '#fff',
fontWeight: 'bold',
textShadowColor: 'rgba(0, 0, 0, 0.4)',
textShadowOffset: {width: -1, height: 1},
textShadowRadius: 10,
},
});
End Result Look like this
Now I want to add an Inner wheel to it Like this

React Native ScrollView inside an Animated.View to make Bumble-like swipe system

I'm currently working on a Bumble-like swipe system so I can swipe horizontally (thanks to an Animated.View and a PanResponder so I can move my view wherever I want), and vertically (because my view is longer than the height of my screen).
After a long day of search, I finally found a solution which permits to know if the user is scrolling horizontally or vertically in the PanResponder, then choose if i block the move or not
The problem is that my canMove() function's console.log is printing null every time so only my vertical scroll is working currently. Otherwise, when i print my scrollType value in the onPanResponderMove, it changes well so I don't understand why my canMove() function gets null
here's my file so you can understand:
const story = useSelector((state) => state.entities.stories[storyId]);
const pan = useRef(new Animated.ValueXY(null, { useNativeDriver: true })).current;
const scrollType = useRef(null);
const checkSwipeDirection = (gestureState) => {
if (
(Math.abs(gestureState.dx) > Math.abs(gestureState.dy * 3))
&& (Math.abs(gestureState.vx) > Math.abs(gestureState.vy * 3))
) {
scrollType.current = 'horizontal';
} else {
scrollType.current = 'vertical';
}
};
const canMove = () => {
console.log('scrollType.current: ', scrollType.current);
if (scrollType.current === 'horizontal') {
return true;
}
return false;
};
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: canMove,
onPanResponderGrant: () => {
pan.setValue({ x: 0, y: 0 });
},
onPanResponderMove: (evt, gestureState) => {
if (!scrollType.current) {
checkSwipeDirection(gestureState);
}
return Animated.event(
[null, { dx: pan.x, dy: pan.y }],
{ useNativeDriver: false },
);
},
onPanResponderRelease: () => {
Animated.spring(pan, {
toValue: 0,
useNativeDriver: false,
}).start();
scrollType.current = null;
},
}),
).current;
return (
<Animated.ScrollView
{...panResponder.panHandlers}
style={{
transform: [{ translateX: pan.x }, { translateY: pan.y },
{
rotate: pan.x.interpolate({
inputRange: [-200, 0, 200], outputRange: ['-20deg', '0deg', '20deg'],
}),
}],
}}
>
<TouchableOpacity activeOpacity={1} style={styles.card}>
<DiscoverCardHeader userId={story.recipient} />
<DiscoverStory
storyId={storyId}
navigation={navigation}
recipientId={story.recipient}
authorId={story.author}
/>
</TouchableOpacity>
</Animated.ScrollView>
);
};
if you need more informations i'm there to give you. hope we'll find a solution ! thanks
Try this, newbies <3
const pan = useRef(new Animated.ValueXY(null, {useNativeDriver: true})).current;
var [direction, setDirection] = useState(0)
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
setDirection(0)
pan.setValue({x: 0, y: 0});
},
onPanResponderMove: (evt, gestureState) => {
if ((direction === 0 || direction === 1) &&
(gestureState.dy > 20 || gestureState.dy < -20)) {
setDirection(1)
pan.setValue({x: 0, y: 0});
} else if ((direction === 0 || direction === 2) &&
(gestureState.dx > 20 || gestureState.dx < -20)) {
setDirection(2)
pan.setValue({x: gestureState.dx, y: 0});
}
},
onPanResponderRelease: () => {
setDirection(0)
pan.setValue({x: 0, y: 0});
},
})
const translate = {
transform: [
...[pan.getTranslateTransform()[0]]
]
}
return (
<Animated.ScrollView scrollEnabled={direction !== 2} >
<Animated.View
{...panResponder.panHandlers}
style={[translate]}
>
<View style={{
backgroundColor: "red",
marginTop: 100
}}>
<View>
<Text> Coucou</Text>
<Text style={{
marginTop: 1000
}}> Coucou juyin le chien</Text>
</View>
</View>
</Animated.View>
</Animated.ScrollView>
);

convert class components to functional components react native

as i am new in react native. i have no much knowledge of class component. i was stuck in code as class components are used in this code but i want to convert them into functional components. anyone please help me to convert this given code into functional component. this is a code of a swipeable card in react native all the given code in class component and use of constructor and this. i want to just convert it into functional component.
//This is an example of Tinder like Swipeable Card//
import React, { Component } from 'react';
//import react in our code.
import {
Platform, StyleSheet, View, Text,
Dimensions, Animated, PanResponder,
} from 'react-native';
//import all the components we are going to use.
const SCREEN_WIDTH = Dimensions.get('window').width;
class SwipeableCard extends React.Component {
constructor() {
super();
this.panResponder;
this.state = {
Xposition: new Animated.Value(0),
RightText: false,
LeftText: false,
};
this.Card_Opacity = new Animated.Value(1);
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => false,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => false,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderMove: (evt, gestureState) => {
this.state.Xposition.setValue(gestureState.dx);
if (gestureState.dx > SCREEN_WIDTH - 250) {
this.setState({
RightText: true,
LeftText: false,
});
} else if (gestureState.dx < -SCREEN_WIDTH + 250) {
this.setState({
LeftText: true,
RightText: false,
});
}
},
onPanResponderRelease: (evt, gestureState) => {
if (
gestureState.dx < SCREEN_WIDTH - 150 &&
gestureState.dx > -SCREEN_WIDTH + 150
) {
this.setState({
LeftText: false,
RightText: false,
});
Animated.spring(
this.state.Xposition,
{
toValue: 0,
speed: 5,
bounciness: 10,
},
{ useNativeDriver: true }
).start();
} else if (gestureState.dx > SCREEN_WIDTH - 150) {
Animated.parallel(
[
Animated.timing(this.state.Xposition, {
toValue: SCREEN_WIDTH,
duration: 200,
}),
Animated.timing(this.Card_Opacity, {
toValue: 0,
duration: 200,
}),
],
{ useNativeDriver: true }
).start(() => {
this.setState({ LeftText: false, RightText: false }, () => {
this.props.removeCard();
});
});
} else if (gestureState.dx < -SCREEN_WIDTH + 150) {
Animated.parallel(
[
Animated.timing(this.state.Xposition, {
toValue: -SCREEN_WIDTH,
duration: 200,
}),
Animated.timing(this.Card_Opacity, {
toValue: 0,
duration: 200,
}),
],
{ useNativeDriver: true }
).start(() => {
this.setState({ LeftText: false, RightText: false }, () => {
this.props.removeCard();
});
});
}
},
});
}
render() {
const rotateCard = this.state.Xposition.interpolate({
inputRange: [-200, 0, 200],
outputRange: ['-20deg', '0deg', '20deg'],
});
return (
<Animated.View
{...this.panResponder.panHandlers}
style={[
styles.card_Style,
{
backgroundColor: this.props.item.backgroundColor,
opacity: this.Card_Opacity,
transform: [
{ translateX: this.state.Xposition },
{ rotate: rotateCard },
],
},
]}>
<Text style={styles.Card_Title}> {this.props.item.card_Title} </Text>
{this.state.LeftText ? (
<Text style={styles.Left_Text_Style}> Left Swipe </Text>
) : null}
{this.state.RightText ? (
<Text style={styles.Right_Text_Style}> Right Swipe </Text>
) : null}
</Animated.View>
);
}
}
export default class App extends React.Component {
constructor() {
super();
this.state = {
Sample_Card_Array: [{
id: '1', card_Title: 'Card 1', backgroundColor: '#FFC107',
}, {
id: '2', card_Title: 'Card 2', backgroundColor: '#ED2525',
}, {
id: '3', card_Title: 'Card 3', backgroundColor: '#E7088E',
}, {
id: '4', card_Title: 'Card 4', backgroundColor: '#00BCD4',
}, {
id: '5', card_Title: 'Card 5', backgroundColor: '#FFFB14',
}],
No_More_Card: false,
};
}
componentDidMount() {
this.setState({
Sample_Card_Array: this.state.Sample_Card_Array.reverse(),
});
if (this.state.Sample_Card_Array.length == 0) {
this.setState({ No_More_Card: true });
}
}
removeCard = id => {
this.state.Sample_Card_Array.splice(
this.state.Sample_Card_Array.findIndex(x => x.id == id),
1
);
this.setState({ Sample_Card_Array: this.state.Sample_Card_Array }, () => {
if (this.state.Sample_Card_Array.length == 0) {
this.setState({ No_More_Card: true });
}
});
};
render() {
return (
<View style={styles.MainContainer}>
{this.state.Sample_Card_Array.map((item, key) => (
<SwipeableCard
key={key}
item={item}
removeCard={this.removeCard.bind(this, item.id)}
/>
))}
{this.state.No_More_Card ? (
<Text style={{ fontSize: 22, color: '#000' }}>No Cards Found.</Text>
) : null}
</View>
);
}
}
const styles = StyleSheet.create({
MainContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingTop: Platform.OS === 'ios' ? 20 : 0,
},
card_Style: {
width: '75%',
height: '45%',
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
borderRadius: 7,
},
Card_Title: {
color: '#fff',
fontSize: 24,
},
Left_Text_Style: {
top: 22,
right: 32,
position: 'absolute',
color: '#fff',
fontSize: 20,
fontWeight: 'bold',
backgroundColor: 'transparent',
},
Right_Text_Style: {
top: 22,
left: 32,
position: 'absolute',
color: '#fff',
fontSize: 20,
fontWeight: 'bold',
backgroundColor: 'transparent',
},
});
the part in the render method is what you return.
to create stateObjects in functional components you will need to use the useState method
const functionalComponent = (props)=>{//props are passed in via props arg...
const defaultState = Xposition: new Animated.Value(0),
RightText: false,
LeftText: false
}
const [state,setState] = useState(defaultState);
... // more stuff
return (
<Animated.View
{...this.panResponder.panHandlers}
style={[
styles.card_Style,
{
backgroundColor: props.item.backgroundColor,
opacity: Card_Opacity,
transform: [
{ translateX: state.Xposition },
{ rotate: rotateCard },
],
},
]}>
<Text style={styles.Card_Title}> {props.item.card_Title} </Text>
{this.state.LeftText ? (
<Text style={styles.Left_Text_Style}> Left Swipe </Text>
) : null}
{this.state.RightText ? (
<Text style={styles.Right_Text_Style}> Right Swipe </Text>
) : null}
</Animated.View>
);
}
you should really go watch some videos on useState, you can be much more granular
to set the state you will need to use the setState method returned from the useState call : setState({..state,{XPosition:55}) or something ... you do the ...state to include the old state values, as the state variable will be overwritten with exactly what you pass in... it wont "update" the existing state it will overwrite it
the next bit is hooking into the functionality in componentDidMount you can do this with useEffect
useEffect(()=>{ // this is setup
// do the stuff from componentDidMount
return ()=>{
// any required teardown can be done here
},[] //[] signifies only do this when component mounts... not every update
);// end useEffect componentDidMount
again there is alot more to useEffect, if you want to do stuff when specific state or props are updated

react-native-gesture-handler pan/drag and rotate wrong values when rotated

I made a single component based on the react-native-gesture-handler examples and they are working pretty well. I can transform, scale, move and rotate the image. But as soon as I rotate the image for 90° as an example, I receive wrong values for translateX and translateY. Moving down will move it right, swiping up will move it left etc.
How do I take the rotation into consideration based on my component.
Please ignore the "tilt" feature, it's not in use for now.
import React from 'react';
import {Animated, StyleSheet} from 'react-native';
import {PanGestureHandler, PinchGestureHandler, RotationGestureHandler, State,} from 'react-native-gesture-handler';
export class PinchableBox extends React.Component {
panRef = React.createRef();
rotationRef = React.createRef();
pinchRef = React.createRef();
dragRef = React.createRef();
constructor(props) {
super(props);
this.state = {
_isMounted: false
};
/* Pinching */
this._baseScale = new Animated.Value(1);
this._pinchScale = new Animated.Value(1);
this._scale = Animated.multiply(this._baseScale, this._pinchScale);
this._lastScale = 1;
this._onPinchGestureEvent = Animated.event(
[{nativeEvent: {scale: this._pinchScale}}],
{useNativeDriver: true}
);
/* Rotation */
this._rotate = new Animated.Value(0);
this._rotateStr = this._rotate.interpolate({
inputRange: [-100, 100],
outputRange: ['-100rad', '100rad'],
});
this._lastRotate = 0;
this._onRotateGestureEvent = Animated.event(
[{nativeEvent: {rotation: this._rotate}}],
{useNativeDriver: true}
);
/* Tilt */
this._tilt = new Animated.Value(0);
this._tiltStr = this._tilt.interpolate({
inputRange: [-501, -500, 0, 1],
outputRange: ['1rad', '1rad', '0rad', '0rad'],
});
this._lastTilt = 0;
this._onTiltGestureEvent = Animated.event(
[{nativeEvent: {translationY: this._tilt}}],
{useNativeDriver: false}
);
this._translateX = new Animated.Value(0);
this._translateY = new Animated.Value(0);
this._lastOffset = {x: 0, y: 0};
this._onGestureEvent = Animated.event(
[
{
nativeEvent: {
translationX: this._translateX,
translationY: this._translateY,
},
},
],
{useNativeDriver: true}
);
}
_onRotateHandlerStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
this._lastRotate += event.nativeEvent.rotation;
this._rotate.setOffset(this._lastRotate);
this._rotate.setValue(0);
}
};
_onPinchHandlerStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
this._lastScale *= event.nativeEvent.scale;
this._baseScale.setValue(this._lastScale);
this._pinchScale.setValue(1);
}
};
_onTiltGestureStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
this._lastTilt += event.nativeEvent.translationY;
this._tilt.setOffset(this._lastTilt);
this._tilt.setValue(0);
}
};
_onHandlerStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
this._lastOffset.x += event.nativeEvent.translationX;
this._lastOffset.y += event.nativeEvent.translationY;
this._translateX.setOffset(this._lastOffset.x);
this._translateX.setValue(0);
this._translateY.setOffset(this._lastOffset.y);
this._translateY.setValue(0);
}
};
render() {
const {image} = this.props;
return (
<PanGestureHandler
ref={this.dragRef}
simultaneousHandlers={[this.rotationRef, this.pinchRef]}
onGestureEvent={this._onGestureEvent}
minPointers={1}
maxPointers={2}
avgTouches
onHandlerStateChange={this._onHandlerStateChange}>
<Animated.View style={styles.wrapper}>
<RotationGestureHandler
ref={this.rotationRef}
simultaneousHandlers={this.pinchRef}
onGestureEvent={this._onRotateGestureEvent}
onHandlerStateChange={this._onRotateHandlerStateChange}>
<Animated.View style={styles.wrapper}>
<PinchGestureHandler
ref={this.pinchRef}
simultaneousHandlers={this.rotationRef}
onGestureEvent={this._onPinchGestureEvent}
onHandlerStateChange={this._onPinchHandlerStateChange}>
<Animated.View style={styles.container} collapsable={false}>
<Animated.Image
resizeMode={"contain"}
style={[
styles.pinchableImage,
{
transform: [
{scale: this._scale},
{rotate: this._rotateStr},
{rotateX: this._tiltStr},
{translateX: this._translateX},
{translateY: this._translateY},
],
},
]}
source={{uri: image}}
/>
</Animated.View>
</PinchGestureHandler>
</Animated.View>
</RotationGestureHandler>
</Animated.View>
</PanGestureHandler>
);
}
}
export default PinchableBox;
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'transparent',
overflow: 'hidden',
alignItems: 'center',
flex: 1,
justifyContent: 'center'
},
pinchableImage: {
...StyleSheet.absoluteFillObject,
},
wrapper: {
flex: 1,
},
});
The solution was actually pretty easy. Instead of adding all computed values to one View, I had to chunk them into a single view per gesture.
import React from 'react';
import {Animated, StyleSheet} from 'react-native';
import {PanGestureHandler, PinchGestureHandler, RotationGestureHandler, State,} from 'react-native-gesture-handler';
export class PinchableBox extends React.Component {
panRef = React.createRef();
rotationRef = React.createRef();
pinchRef = React.createRef();
dragRef = React.createRef();
constructor(props) {
super(props);
this.state = {
_isMounted: false
};
/* Pinching */
this._baseScale = new Animated.Value(1);
this._pinchScale = new Animated.Value(1);
this._scale = Animated.multiply(this._baseScale, this._pinchScale);
this._lastScale = 1;
this._onPinchGestureEvent = Animated.event(
[{nativeEvent: {scale: this._pinchScale}}],
{useNativeDriver: true}
);
/* Rotation */
this._rotate = new Animated.Value(0);
this._rotateStr = this._rotate.interpolate({
inputRange: [-100, 100],
outputRange: ['-100rad', '100rad'],
});
this._lastRotate = 0;
this._onRotateGestureEvent = Animated.event(
[{nativeEvent: {rotation: this._rotate}}],
{useNativeDriver: true}
);
/* Tilt */
this._tilt = new Animated.Value(0);
this._tiltStr = this._tilt.interpolate({
inputRange: [-501, -500, 0, 1],
outputRange: ['1rad', '1rad', '0rad', '0rad'],
});
this._lastTilt = 0;
this._onTiltGestureEvent = Animated.event(
[{nativeEvent: {translationY: this._tilt}}],
{useNativeDriver: true}
);
this._translateX = new Animated.Value(0);
this._translateY = new Animated.Value(0);
this._lastOffset = {x: 0, y: 0};
this._onGestureEvent = Animated.event(
[
{
nativeEvent: {
translationX: this._translateX,
translationY: this._translateY,
},
},
],
{useNativeDriver: true}
);
}
_onRotateHandlerStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
this._lastRotate += event.nativeEvent.rotation;
this._rotate.setOffset(this._lastRotate);
this._rotate.setValue(0);
}
};
_onPinchHandlerStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
this._lastScale *= event.nativeEvent.scale;
this._baseScale.setValue(this._lastScale);
this._pinchScale.setValue(1);
}
};
_onTiltGestureStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
this._lastTilt += event.nativeEvent.translationY;
this._tilt.setOffset(this._lastTilt);
this._tilt.setValue(0);
}
};
_onHandlerStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
this._lastOffset.x += event.nativeEvent.translationX;
this._lastOffset.y += event.nativeEvent.translationY;
this._translateX.setOffset(this._lastOffset.x);
this._translateX.setValue(0);
this._translateY.setOffset(this._lastOffset.y);
this._translateY.setValue(0);
}
};
render() {
const {image, children} = this.props;
return (
<React.Fragment>
<PanGestureHandler
ref={this.dragRef}
simultaneousHandlers={[this.rotationRef, this.pinchRef]}
onGestureEvent={this._onGestureEvent}
minPointers={2}
maxPointers={2}
avgTouches
onHandlerStateChange={this._onHandlerStateChange}>
<Animated.View style={[
styles.wrapper,
{
transform: [
{translateX: this._translateX},
{translateY: this._translateY},
],
},
]}>
<RotationGestureHandler
ref={this.rotationRef}
simultaneousHandlers={this.pinchRef}
onGestureEvent={this._onRotateGestureEvent}
onHandlerStateChange={this._onRotateHandlerStateChange}>
<Animated.View style={[
styles.wrapper,
{
transform: [
{rotate: this._rotateStr},
],
},
]}
>
<PinchGestureHandler
ref={this.pinchRef}
simultaneousHandlers={this.rotationRef}
onGestureEvent={this._onPinchGestureEvent}
onHandlerStateChange={this._onPinchHandlerStateChange}>
<Animated.View style={[
styles.container,
{
transform: [
{scale: this._scale},
],
},
]} collapsable={false}>
<Animated.Image
resizeMode={"contain"}
style={[
styles.pinchableImage,
]}
source={{uri: image}}
/>
</Animated.View>
</PinchGestureHandler>
</Animated.View>
</RotationGestureHandler>
</Animated.View>
</PanGestureHandler>
{ children }
</React.Fragment>
);
}
}
export default PinchableBox;
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'transparent',
overflow: 'hidden',
alignItems: 'center',
flex: 1,
justifyContent: 'center'
},
pinchableImage: {
backgroundColor: "transparent",
...StyleSheet.absoluteFillObject,
},
wrapper: {
flex: 1,
},
});

How to get and pass the details of the active slide in react native using react-native-snap-carousel

I need to pass the details of the currently active slide to another js file. how can I do this (there are 8 slides)?
The details that I need to pass is the slide's name and Index,
Here is my carousel js file :
import React, { Component } from 'react';
import { Dimensions, View, Image } from 'react-native';
import Carousel from 'react-native-snap-carousel';
const { height, width } = Dimensions.get('window');
class TeamScroll extends Component {
render() {
return (
<View >
<View style={{ transform: [{ rotate: '-14deg' }] }}>
<Carousel
ref={(c) => { this.props.carouselRef = c; }}
inactiveSlideOpacity={0.6}
inactiveSlideScale={0.65}
firstItem={1}
sliderWidth={width}
itemWidth={width / 3} >
<Image
source={require('./Images/logo-chepauk.png')}
style={styles.logoStyle} />
<Image
source={require('./Images/logo-dindigul.png')}
style={styles.logoStyle} />
<Image
source={require('./Images/logo-kanchi.png')}
style={styles.logoStyle} />
<Image
source={require('./Images/logo-karaikudi.png')}
style={styles.logoStyle} />
<Image
source={require('./Images/logo-kovai.png')}
style={styles.logoStyle} />
<Image
source={require('./Images/logomadurai.png')}
style={styles.logoStyle} />
<Image
source={require('./Images/logothiruvallur.png')}
style={styles.logoStyle} />
<Image
source={require('./Images/logotuti.png')}
style={styles.logoStyle} />
</Carousel>
</View>
</View>
);
}
}
const styles = {
logoStyle: {
transform: [{ rotate: '14deg' }],
width: width / 3,
height: width / 3
}
};
export default TeamScroll;
Here is one of the files where I need to use these details
import React, { Component } from 'react';
import { Image, Text, View, TouchableWithoutFeedback, Animated, Dimensions } from 'react-native';
import { Actions } from 'react-native-router-flux';
import TeamScroll from './TeamScroll';
const a = require('./Images/over3_selected.png');
const b = require('./Images/over3.png');
const c = require('./Images/over5_selected.png');
const d = require('./Images/over5.png');
const e = require('./Images/over10_selected.png');
const f = require('./Images/over10.png');
const Sound = require('react-native-sound');
const btnSound = new Sound('btn_sound.mp3', Sound.MAIN_BUNDLE);
const w = Dimensions.get('window').width;
const h = Dimensions.get('window').height;
class ChallengeScreen extends Component {
state = {
threePressed: false,
fivePressed: false,
tenPressed: false,
}
componentWillMount() {
this.slide1 = new Animated.Value(0);
this.slide2 = new Animated.Value(0);
this.slide3 = new Animated.Value(0);
this.ball1();
this.ball2();
this.ball3();
}
ball1() {
Animated.timing(
this.slide1, {
delay: 100,
toValue: -(w / 2.57),
duration: 700,
}
).start();
}
ball2() {
Animated.timing(
this.slide2, {
delay: 200,
toValue: -(w / 2.25),
duration: 900,
}
).start();
}
ball3() {
Animated.timing(
this.slide3, {
delay: 300,
toValue: -(w / 2),
duration: 1100,
}
).start();
}
render() {
return (
<Image
source={require('./Images/bg_inner.png')} style={styles.backgroundStyle}>
<Text style={styles.chooseteamtextStyle}>
CHOOSE YOUR TEAM
</Text>
<Image source={require('./Images/team-logo-band.png')} style={styles.band1Style}>
<TeamScroll carouselRef />
</Image>
<Text style={styles.opponentStyle}>
YOUR OPPONENT
</Text>
<Image source={require('./Images/team-logo-band.png')} style={styles.band2Style}>
<TeamScroll carouselRef />
</Image>
<Text style={styles.overstextStyle}>
OVERS
</Text>
<View style={styles.viewStyle}>
<TouchableWithoutFeedback
onPress={() => {
btnSound.play();
playFunc(3, this.props.challenge); }
}
onPressIn={() => {
this.setState({ threePressed: true });
}}
onPressOut={() => {
this.setState({ threePressed: false });
}}
>
<Animated.Image source={this.state.threePressed ? a : b}
style={[styles.over3Style, { transform: [{ translateY: this.slide1 }] }]} />
</ TouchableWithoutFeedback>
<TouchableWithoutFeedback
onPress={() => {
btnSound.play();
playFunc(5, this.props.challenge); }
}
onPressIn={() => {
this.setState({ fivePressed: true });
}}
onPressOut={() => {
this.setState({ fivePressed: false });
}}>
<Animated.Image source={this.state.fivePressed ? c : d}
style={[styles.over5Style, { transform: [{ translateY: this.slide2 }] }]} />
</ TouchableWithoutFeedback>
<TouchableWithoutFeedback
onPress={() => {
btnSound.play();
playFunc(10, this.props.challenge); }
}
onPressIn={() => {
this.setState({ tenPressed: true });
}}
onPressOut={() => {
this.setState({ tenPressed: false });
}}>
<Animated.Image source={this.state.tenPressed ? e : f}
style={[styles.over10Style, { transform: [{ translateY: this.slide3 }] }]} />
</ TouchableWithoutFeedback>
</View>
</ Image>
);
}
}
function playFunc(num, param) {
if (num === 3 && param === 'Computer') {
Actions.screen4({ balls: 18 });
}
else if (num === 5 && param === 'Computer') {
Actions.screen4({ balls: 30 });
}
else if (num === 10 && param === 'Computer') {
Actions.screen4({ balls: 60 });
}
else if (num === 3 && param === 'Team') {
Actions.screen3({ balls: 18 });
}
else if (num === 5 && param === 'Team') {
Actions.screen3({ balls: 30 });
}
else if (num === 10 && param === 'Team') {
Actions.screen3({ balls: 60 });
}
}
const styles = {
viewStyle: {
flexDirection: 'row',
justifyContent: 'flex-start'
},
backgroundStyle: {
flex: 1,
width: w,
height: h,
flexWrap: 'wrap',
},
chooseteamtextStyle: {
textAlign: 'center',
marginTop: h / 6.57,
fontSize: 22,
color: 'white',
fontFamily: 'Switzerland-Cond-Black-Italic',
transform: [{ rotate: '-14deg' }]
},
band1Style: {
resizeMode: 'stretch',
width: (w / 0.947),
height: (h / 3.93),
},
opponentStyle: {
textAlign: 'center',
marginTop: -(h / 59.2),
fontSize: 22,
color: 'white',
fontFamily: 'Switzerland-Cond-Black-Italic',
transform: [{ rotate: '-15deg' }]
},
band2Style: {
resizeMode: 'stretch',
width: (w / 0.947),
height: (h / 4),
},
overstextStyle: {
textAlign: 'center',
bottom: (h / 59.2),
fontSize: 22,
color: 'white',
fontFamily: 'Switzerland-Cond-Black-Italic',
transform: [{ rotate: '-15deg' }]
},
over3Style: {
flexDirection: 'row',
alignItems: 'flex-start',
width: (w / 4.5),
height: (h / 7.4),
top: (h / 3.482),
left: (w / 5.142),
},
over5Style: {
flexDirection: 'row',
alignItems: 'center',
width: (w / 4.5),
height: (h / 7.4),
bottom: -(h / 3.48),
left: (h / 8.45)
},
over10Style: {
flexDirection: 'row',
alignItems: 'flex-end',
width: (w / 4.5),
height: (h / 7.4),
top: (h / 3.48),
right: -(w / 5.42)
}
};
export default ChallengeScreen;
I have tried using state and props for doing it, and also using getters like currentIndex using carousel's reference but couldn't get the details
What about adding a dedicated prop to <TeamScroll />?
TeamScroll.js
class TeamScroll extends Component {
static propTypes = {
snapCallback: PropTypes.func
}
static defaultProps = {
snapCallback: () => {}
}
constructor (props) {
super(props);
this._onSnapToItem = this._onSnapToItem.bind(this);
}
_onSnapToItem (index) {
this.props.snapCallback(index, this.state.customData);
}
render() {
return (
<View >
<View style={{ transform: [{ rotate: '-14deg' }] }}>
<Carousel onSnapToItem={this._onSnapToItem}>
// images
</Carousel>
</View>
</View>
);
}
}
ChallengeScreen.js
class ChallengeScreen extends Component {
onSnap (index, data) {
console.log('CAROUSEL INDEX', { index, data });
}
render() {
return (
<Image source={require('./Images/team-logo-band.png')} style={styles.band2Style}>
<TeamScroll carouselRef snapCallback={onSnap} />
</Image>
);
}
}
I'm using this
class TeamScroll extends Component {
constructor(props) {
super(props);
this.state = {
currentIndex: 0,
};
}
changePage(nextIndex, isLast) {
this.setState({ currentIndex: nextIndex });
this.props.onChangePage(nextIndex + 1, isLast);
}
render() {
return (
<Page>
<Carousel
ref={(carousel) => { this.carousel = carousel; }}
firstItem={this.state.currentIndex}
onSnapToItem={(index) => this.changePage(index, index === screens.length - 1)}
data={screens}
renderItem={this.renderCarouselItem}
/>
);
}
}
Note that I'm using the new syntax introduced in version 3, but it works as well in version 2.
The class has onChangePage prop that gets called when you snap to another item.
You can use the onChangePage with
<TeamScroll onChangePage={(pageIndex, isLastPage) => {
// do something here, maybe
this.setState({ currentPage: pageIndex });
}} />

Categories

Resources