I am creating a poker table using react native and I want that on the click of a chair animation should happen(arrangement of chairs around the table).
But animation does not work on clicking on the first time, while it works when I click 2nd time.
You can see there are 8 chairs around the table what I want is that whenever I click on any table, the rotation should take place in such a way that the table I clicked on should replace the green table(bottom)
The problem is that on the first click, the code inside useEffect works but still animation does not occur.
Here is the code
import {
Animated,
} from 'react-native';
function HomeScreen() {
const [shiftBy, setSiftBy] = React.useState(0);
const [p1InitialPosition] = React.useState(new Animated.ValueXY({ x: 180, y: 30 }));
const [p2InitialPosition] = React.useState(new Animated.ValueXY({ x: 325, y: 125 }));
const [p3InitialPosition] = React.useState(new Animated.ValueXY({ x: 338, y: 230 }));
const [p4InitialPosition] = React.useState(new Animated.ValueXY({ x: 335, y: 370 }));
const [p5InitialPosition] = React.useState(new Animated.ValueXY({ x: 180, y: 470 }));
const [p6InitialPosition] = React.useState(new Animated.ValueXY({ x: 30, y: 370 }));
const [p7InitialPosition] = React.useState(new Animated.ValueXY({ x: 30, y: 230 }));
const [p8InitialPosition] = React.useState(new Animated.ValueXY({ x: 40, y: 125 }));
const chairs = React.useMemo(() => {
return [
p1InitialPosition,
p2InitialPosition,
p3InitialPosition,
p4InitialPosition,
p5InitialPosition,
p6InitialPosition,
p7InitialPosition,
p8InitialPosition,
];
}, [
p1InitialPosition,
p2InitialPosition,
p3InitialPosition,
p4InitialPosition,
p5InitialPosition,
p6InitialPosition,
p7InitialPosition,
p8InitialPosition,
]);
// updated/animated positions
React.useEffect(() => {
const np = [];
for (let i = 0; i < chairs.length; i ) {
console.log(chairs[i], chairs[(i shiftBy) % 8]);
np.push(
Animated.timing(chairs[i], {
toValue: chairs[(i shiftBy) % 8],
duration: 800,
useNativeDriver: true,
}),
);
}
Animated.parallel(np).start();
console.log('');
}, [shiftBy]);
return (
<SafeAreaView style={styles.container}>
<Image
onLayout={event => {
const layout = event.nativeEvent.layout;
console.log('height:', layout.height);
console.log('width:', layout.width);
console.log('x:', layout.x);
console.log('y:', layout.y);
}}
source={require('../../assets/image/table/table.png')}
style={{
height: '100%',
width: '100%',
}}
/>
{/* p1 */}
<Animated.View
style={{
position: 'absolute',
transform: [{ translateX: p1InitialPosition.x }, { translateY: p1InitialPosition.y }],
}}>
<TouchableOpacity
// disabled={selectedChair}
onPress={() => setSiftBy(4)}>
<Icon name="chair" size={30} color="blue" />
</TouchableOpacity>
</Animated.View>
{/* p2 */}
<Animated.View
style={{
position: 'absolute',
transform: [{ translateX: p2InitialPosition.x }, { translateY: p2InitialPosition.y }],
}}>
<TouchableOpacity
// disabled={selectedChair}
onPress={() => setSiftBy(3)}>
<Icon name="chair" size={30} color="skyblue" />
</TouchableOpacity>
</Animated.View>
{/* p3 */}
{/* p4 */}
{/* p5 */}
{/* p6 */}
{/* p7 */}
{/* p8 */}
</SafeAreaView>
);
}
export default HomeScreen;
this is the link to complete the code for the screen- snack.expo.dev/sd7yF5CzZ
Please help.
CodePudding user response:
The solution is to add useLayoutEffect
in your code :
React.useLayoutEffect(() => {
const np = [];
for (let i = 0; i < chairs.length; i ) {
console.log(chairs[i], chairs[(i shiftBy) % 8]);
np.push(
Animated.timing(chairs[i], {
toValue: chairs[(i shiftBy) % 8],
duration: 800,
useNativeDriver: true,
}),
);
}
Animated.parallel(np).start();
console.log('');
}, [shiftBy]);
Explaination
Since useEffect
callbacks are called after the render commit phase and hence after the render cycle, and LayoutAnimation.configureNext
is meant to "prepare" the next render, it is already too late to call it inside of useEffect
.
So the solution is to use useLayoutEffect
along with useEffect
to ensure that animation will run on first render