React Native Animated 在第一次点击时不起作用

React Native Animated don't work on first click

我正在使用 React Native 创建一个扑克牌 table,我希望在点击椅子动画时发生(椅子围绕 table 排列)。

但是动画在第一次点击时不起作用,而在我点击第二次时动画起作用。

请查看下图

你可以看到 table 周围有 8 把椅子 我想要的是每当我点击任何 table 时,旋转应该以这样的方式进行 table 我点击应该替换掉绿色的table(下)

问题是在第一次点击时,useEffect 中的代码有效,但仍然没有出现动画。

这是代码

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;

这是 link 完成屏幕代码- snack.expo.dev/sd7yF5CzZ

请帮忙。

解决方法是在你的代码中添加useLayoutEffect

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]);

说明

由于 useEffect 回调在渲染提交阶段之后调用,因此在渲染周期之后调用,并且 LayoutAnimation.configureNext 意味着“准备”下一个渲染,调用它已经太晚了useEffect.

里面

所以解决方案是使用 useLayoutEffectuseEffect 来确保动画在第一次渲染时 运行