nandorojo / moti

🐼 The React Native (+ Web) animation library, powered by Reanimated 3.
https://moti.fyi
MIT License
3.9k stars 120 forks source link

Animation starting right away and firing onDidAnimate even if nothing moved #317

Closed MSchmidt closed 8 months ago

MSchmidt commented 8 months ago

Is there an existing issue for this?

Do you want this issue prioritized?

Current Behavior

There is a MoviView <MotiView animate={{ left: pos.x, top: pos.y }} onDidAnimate={() => {}}. pos is a React state. onDidAnimate will fire after the animation even though nothing has moved at all. Since pos is still at the initial values. 0 in my case.

Expected Behavior

A bit tricky. At first I thought onDidAnimate should not fire if nothing moved. But one can argue that a animation to existing values is still an animation. So maybe onDidAnimate should have something similar to the second finished argument that indicates if something happened at all. An alternative could be a autoStart attribute to control if anything should happen right after rending at all.

Steps To Reproduce

No response

Versions

- Moti: 0.25.3
- Reanimated: 3.4.2
- React Native: 0.72.5

Screenshots

No response

Reproduction

This is rather conceptual and hope the description is clear enough.

nandorojo commented 8 months ago

Please post all the code, I’m not sure what x and y are

nandorojo commented 8 months ago

It is interesting for it to fire on mount…it simply relies on reanimated for that

MSchmidt commented 8 months ago
export default function Item() {
  const [pos, setPos] = useState({ x: 0, y: 0 })

  return (
    <MotiView
      style={{ width: 100, height: 50, backgroundColor: 'red' }}
      animate={{ translateX: pos.x, translateY: pos.y }}
      onDidAnimate={(_, done) => {
        console.log(`onDidAnimate: ${done}`)
      }} />
  )
}

Output for this is:

onDidAnimate: true
onDidAnimate: true
nandorojo commented 8 months ago

Got it, you may want to print the other arguments too as they could be useful for knowing where it animated to

MSchmidt commented 8 months ago

Sure! I've also added the from to make sure it goes from 0 to 0. No difference.

<MotiView
  style={{ width: 100, height: 50, backgroundColor: 'red' }}
  from={{ translateX: 0, translateY: 0 }}
  animate={{ translateX: pos.x, translateY: pos.y }}
  onDidAnimate={(what, done, value, eventDict) => {
    console.log(`onDidAnimate: ${what}, ${done}, ${value}, ${eventDict.attemptedValue}`)
  }} />
 LOG  onDidAnimate: translateX, true, undefined, 0
 LOG  onDidAnimate: translateY, true, undefined, 0
nandorojo commented 8 months ago

Got it, yeah I would rely on the attemptedValue for this I think

MSchmidt commented 8 months ago

Yes sounds good. So it's not really a bug then? I'm fine to work around this for my use case but thought I would report it anyways. It's not really intuitive to get onDidAnimate triggered when nothing visible happened.

nandorojo commented 8 months ago

I agree it’s not that intuitive, but given that we allow any number of keys in the animate and from prop (which can change at any time) this is the way of making sure it’s the most correct I believe. I’m also just following reanimated’s behavior which does this (albeit a bit aggressively). Because each property handles its own callbacks I think it’s the best we can do. Hope this makes sense

MSchmidt commented 8 months ago

Yes makes sense. I'll just close this then. Maybe it's worth a mention in the docs though? Already very good to know that each property has it's own callback.

nandorojo commented 8 months ago

Yeah agreed would be good to mention this