React-native-reanimated: question : how to call js callback like setState after runTiming()

Created on 16 Oct 2019  路  15Comments  路  Source: software-mansion/react-native-reanimated

my use-case is to call a setState after runTiming or runSpring was done springing the value.
here is my code


function runSpring({ translateY, velocityY, state: _state }) {
  const clock = new Clock();
  const state = {
    finished: new Value(0),
    velocity: new Value(0),
    position: new Value(0),
    time: new Value(0)
  };
  const config = {
    stiffness: new Value(10),
    mass: new Value(1),
    damping: new Value(10),
    overshootClamping: false,
    restSpeedThreshold: 0.001,
    restDisplacementThreshold: 0.001
  };
  const configB = SpringUtils.makeConfigFromBouncinessAndSpeed({
    ...SpringUtils.makeDefaultConfig(),
    bounciness: 10,
    speed: 2
  });
  const configC = SpringUtils.makeConfigFromOrigamiTensionAndFriction({
    ...SpringUtils.makeDefaultConfig(),
    tension: 100,
    friction: new Value(40)
  });
  // startClock(clock);
  return block([
    cond(lessOrEq(translateY, 0), [
      cond(
        lessOrEq(velocityY, -300),
        [
          startClock(clock),
          spring(clock, state, { ...config, toValue: -DeviceHeight })
        ],
        [
          cond(
            eq(_state, State.END),
            [startClock(clock), spring(clock, state, config)],

            [
              set(state.position, translateY)
              // debug("clock  2 happened", eq(_state, State.ACTIVE))
            ]
          )
        ]
      )
    ]),
    state.position
  ]);
}

port default class ExampleTab extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      currentItemIndex: 0

    };
    this._translateY = new Value(0);
    this._state = new Value(State.UNDETERMINED);
    this._velocityY = new Value(0);
    this.currentCardVelocity = new Value(0);
    this.translateY = runSpring({
      translateY: this._translateY,
      velocityY: this._velocityY,
      state: this._state
    });
  }

  render(){
 return (
        <PanGestureHandler
              key={pIndex}
              {...gestureEvent}
              // onHandlerStateChange={this.handleGState}
              enabled={indexDistance === 0}
            >
              <Animated.View
                style={{
                  flex: 1,
                  shadowOffset: { height: 3 },
                  elevation: 3,
                  // borderWidth: 0.5,
                  borderColor: "black",
                  borderRadius: 25,
                  shadowColor: "black",
                  shadowOpacity: 0.5,
                  justifyContent: "flex-end",
                  transform: [
                    {
                      translateY:
                        indexDistance === 0
                          ? this.translateY
                          : isPrevCard
                          ? prevCardTranslateY
                          : translateY
                    }
                  ],
                  // opacity: animatedOpacity,
                  marginHorizontal: multiply(indexDistance, secondCardGrowth),
                  ...StyleSheet.absoluteFillObject
                }}
              >
              </Animed.View>
           </PanGestureHandler>
        ) }
  }
}
鉂換uestion

Most helpful comment

You can do it like below.

Using conditionals with the finished state at the end of the block and the call function to do a callback from js side:

[ ...previous block animated events...,
  cond(state.finished, 
     call([...your params here], (...your params) => {...function })
  )
]

All 15 comments

You can do it like below.

Using conditionals with the finished state at the end of the block and the call function to do a callback from js side:

[ ...previous block animated events...,
  cond(state.finished, 
     call([...your params here], (...your params) => {...function })
  )
]

how to check for state.finished ? its a value(0)

You are passing the state object as param in spring function. Technically these values will be updated while the clock is running.

i tried ,. but that callback is getting called continuously and endlessly


  return block([
    cond(lessOrEq(translateY, 0), [
      cond(
        lessOrEq(velocityY, -3300),
        [
          startClock(clock),
          spring(clock, state, { ...config, toValue: -DeviceHeight })
        ],
        [
          cond(
            eq(_state, State.END),
            [
              startClock(clock),
              spring(clock, state, { ...config, toValue: 0 })
              // stopClock(clock)
            ],

            [
              set(state.position, translateY)
              // debug("clock  2 happened", eq(_state, State.ACTIVE))
            ]
          )
        ]
      )
    ]),
    // cond(state.finished, [stopClock(clock), call([state.finished], callback)]),
    state.position
  ]);

tell me my mistake

Hmmmm, can you create a sample in snack? Because visually I didn't find any errors in your code.

i cant. this is a commercial product.

but i noticed its not mentioned to call startClock(clock) manually. But for me , it is not animating value if i didn't started it manually. do you think it the cause ? @brunocrpontes

Not exactly @vamshi9666, clocks are useful when the imperative animations can't cover the behaviour what you're looking for. Anyway I think it's just a logic mistake, but without an example or a description is hard to figure out.

At the end, I'm 100% sure which my first answer solve you issue question about the callback, because the example in read-me show a similar situation (call something when the animation is done) but instead a call it's a debug function.

```.ts

...previous code

timing(clock, state, config),
// if the animation is over we stop the clock
cond(state.finished, debug('stop clock', stopClock(clock))),
// we made the block return the updated position
state.position,

```

Maybe it's because you need to reset the state values if your clock isn't running? These nodes are continually evaluated, as far as I understand.

return block([
  clockRunning(clock),
      [
        // if the clock is already running we update the toValue, in case a new dest has been passed in
        set(config.toValue, toValue)
      ],
      [
        // if the clock isn't running we reset all the animation params and start the clock
        set(state.finished, 0),
        set(state.time, 0),
        set(state.frameTime, 0),
        set(config.toValue, toValue),
        startClock(clock)
      ]
    ),
    timing(clock, state, config),
    cond(state.finished, [stopClock(clock), /* do finished stuff */]),
    state.position
  ]);

So I'm not the only one...

Hi @vamshi9666,

your first question was answered by @brunocrpontes.

Your second question is a bit unclear though - could you provide a minimal reproducible example on snack with a detailed explanation of what you want to achieve? It's hard to answer that directly judging by code.

Thanks for understanding and have a nice day!

Hi @jakub-gonet, I'm facing the second problem, how can I make a callback to be called only once after a condition is met. For example:

cond(eq(state, State.ACTIVE), [call([someAnimValue], handlefFinished)])

Because as I saw in the the document, it said If one of the nodes from argsNodes array updates, callback will be called, but I don't want the callback to be called again and again if my someAnimValue is changed, how can I do it?

Sorry for the late reply,
you can use some variable to 'block' it.
Something like:

Cond(and(state, flag), action, set(flag, 1))

Maybe it's because you need to reset the state values if your clock isn't running? These nodes are continually evaluated, as far as I understand.

return block([
  clockRunning(clock),
      [
        // if the clock is already running we update the toValue, in case a new dest has been passed in
        set(config.toValue, toValue)
      ],
      [
        // if the clock isn't running we reset all the animation params and start the clock
        set(state.finished, 0),
        set(state.time, 0),
        set(state.frameTime, 0),
        set(config.toValue, toValue),
        startClock(clock)
      ]
    ),
    timing(clock, state, config),
    cond(state.finished, [stopClock(clock), /* do finished stuff */]),
    state.position
  ]);

This is only happening in React Hook, but not class component. setState in class component works ok

To anyone it might help.

  return block([
    cond(not(clockRunning(clock)), [
      set(state.finished, 0),
      set(state.time, 0),
      set(state.frameTime, 0),
      set(state.position, from),
      set(config.toValue, to),
      startClock(clock),
    ]),
    timing(clock, state, config),
    cond(state.finished, debug('stop clock', stopClock(clock))),
    cond(
      state.finished,
      call([state.position], (value) => {
        if (typeof callback === 'function') callback(value[0]);
      }),
    ),
    state.position,
  ]);

stopping the clock then calling the callback worked.
it gets called once after the animation ends.

Note: if you do not stop the clock the callback will get called continuously every time the duration you set elapses. e.g every 500 ms.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mrousavy picture mrousavy  路  3Comments

ArsalanCsquare picture ArsalanCsquare  路  3Comments

WeTruck picture WeTruck  路  3Comments

colinux picture colinux  路  3Comments

nextriot picture nextriot  路  3Comments