理解 react-native-reanimated 执行

Bib*_*ibs 9 react-native expo

我是新手,react-native-reanimated并试图了解它的工作原理。下面的代码在屏幕中间呈现一个框。在初始渲染中,该框向右平移 4 秒,之后其位置将重置为屏幕中间。

... imports omitted for brevity

export default class App extends React.Component {
  state = {
    clock: new Clock(),
    translation: new Value(0),
  };

  onPress = () => {
    startClock(this.state.clock);
  };

  getTranslation = (clock, translation) => {
    const state = {
      finished: new Value(0),
      position: translation,
      time: new Value(0),
      frameTime: new Value(0),
    };
    const config = {
      duration: 4000,
      toValue: new Value(300),
      easing: Easing.inOut(Easing.ease),
    };
    return block([
      cond(clockRunning(clock), 0, [
        set(state.finished, 0),
        set(state.position, 0),
        set(state.time, 0),
        set(state.frameTime, 0),
        startClock(clock),
      ]),
      timing(clock, state, config),
      cond(state.finished, set(state.position, 0)),
      state.position,
    ]);
  };

  render() {
    const translation = this.getTranslation(
      this.state.clock,
      this.state.translation
    );
    return (
      <View style={styles.container}>
        <TouchableOpacity onPress={this.onPress}>
          <Animated.View
            style={{
              transform: [
                {
                  translateX: translation,
                },
              ],
              width: 100,
              height: 100,
              backgroundColor: "tomato",
            }}
          />
        </TouchableOpacity>
      </View>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:

1) 为什么这个框只在初始渲染时向右平移?是什么阻止动画重复?

2) onPress 处理程序不会重新启动动画。为什么?

Kak*_*kar 16

我也在学习react native reanimated,但我会尽量回答你的问题。如果有人想编辑答案,请自由编辑。

  1. 为什么盒子只在初始渲染时向右平移?是什么阻止动画重复?

当组件安装时,该translateX值为 0,它将位于屏幕的中心(因为正如您所说,它styles.container可能具有将其子组件居中的样式)。

并且由于您要求从getTranslationrender 方法中的函数进行翻译,因此当组件第一次渲染时,将getTranslation调用该函数并评估节点块:

  1. block列表中的第一个节点执行,即cond块。它检查时钟是否正在运行。
    • 如果时钟正在运行,那么什么都不做。
    • 否则,设置timing状态和配置。请记住,state.position设置为 0 并且也config.toValue已经设置为 300。并启动时钟。
  2. 定时函数被赋予状态及其配置。
    • 它更新一个时间范围内的位置。在这里,由于您已将 设置state.position为 0 和config.toValue300,因此state.position将更新为 1(取决于config.duration)。
    • 此外,如果时钟正在运行,它会将回调排入队列以在下一帧中进行评估。
  3. 在块列表的倒数第二个节点内,它检查状态finished值是否为 1。
    • 当动画完成时,或者更好的是,当 state 的position值等于 config 的值时toValuetiming函数将 的值更新finished为 1。
    • 因此,当动画完成时,状态的position值再次设置为 0。这意味着位置重置为其原始位置。
  4. 在最后一个节点上,状态position被返回给渲染方法,translation以便为变换保持不变。

    由于时钟开始,动画还没有完成,节点块一次又一次地运行。意思是在下一帧,该state.position值将是 2,然后是 3,依此类推,直到state.position等于 300 ( config.toValue)。因此,该框将从屏幕的中心移到右侧。但是,如果您将 设置config.toValue为 -300,则该框将从屏幕的中心移至左侧。

    最后,当动画完成时,块的第 3 个节点等于 true,并且state.positionis 再次变为 0(在屏幕中央)。

    并且由于您尚未停止时钟(可以使用 完成stopClock(clock)),因此要检查的第一个节点clockRunning(clock)始终为真。同样要重复动画,您必须timing在动画完成后重置所有状态和配置。

    所以你必须改变你的节点块:

            return block([
                cond(
                    clockRunning(clock),
                    [
                        debug('clock is running', clock)
                    ],
                    [
                        debug('clock is NOT running', clock),
                        startClock(clock),
                    ]
                ),
                timing(clock, state, config),
                cond(
                    state.finished,
                    [
                        stopClock(clock),
                        set(state.finished, 0),
                        set(state.position, 0),
                        set(state.time, 0),
                        set(state.frameTime, 0),
                        startClock(clock),
                    ]
                ),
                state.position,
            ]);
    
    Run Code Online (Sandbox Code Playgroud)
  1. onPress 处理程序不会重新启动动画。为什么?

因为时钟没有停止。也因为状态和配置没有被重置。因此,要在按下时启动动画,可以通过多种不同的方式来完成。我将向您展示如何以其中一种方式进行操作,通过这种方式,您可能会更多地了解react-native-reanimatedreact-native-gesture-handler使用纯原生动画,而无需过桥。

const getTranslation = ({clock, gestureState, translation}) => {
    const state = {
        finished: new Value(0),
        position: translation,
        time: new Value(0),
        frameTime: new Value(0),
    };
    const config = {
        duration: 2000,
        toValue: new Value(150),
        easing: Easing.inOut(Easing.ease),
    };
    return block([
        cond(
            clockRunning(clock),
            [
                debug('clock is running', clock)
            ],
            [
                debug('clock is NOT running', clock),
                set(state.finished, 0),
                set(state.position, 0),
                set(state.time, 0),
                set(state.frameTime, 0),
                startClock(clock),
            ]
        ),
        timing(clock, state, config),
        cond(
            state.finished,
            stopClock(clock)
        ),
        state.position
    ])
};

export default class App extends React.Component {

    gestureState = new Value(-1)

    clock = new Clock()

    translation = cond(
        eq(this.gestureState, State.ACTIVE), // when you start drag, the state will be ACTIVE
        [
            debug('active', this.gestureState, State.ACTIVE),
            getTranslation({clock: this.clock, translation: new Value(0)})
        ],
        [
            debug('not active', this.gestureState, State.ACTIVE)
        ],
    )

    onStateChange = event([
        {
            nativeEvent: {
                state: this.gestureState
            }
        }
    ])

    render() {

        return (
            <View style={styles.container}>
                <PanGestureHandler
                    onGestureChange={this.onStateChange}
                    onHandlerStateChange={this.onStateChange}>
                    <Animated.View style={[
                        styles.box,
                        {
                            transform: [{
                                translateX: this.translation
                            }]
                        }
                    ]}/>
                </PanGestureHandler>
            </View>
        )
    }
}
Run Code Online (Sandbox Code Playgroud)