Skip to main content
4mo ago

React Rive Animations getting stuck

Hi there. I'm building a React app with various Rive animations, and loving it. Sporadically the animations get stuck. It's like they are pausing for some undetermined reason. The onPause callback is not being fired. I have no way to know when it happens programmatically, and thus no way to make them play again.

It's difficult to reliably reproduce.

In my scoreboard component, player cards leap frog to the top. The Rive Avatars that are rendered underneath the leap frogging player cards pretty consistently freeze, and sometimes their animation begin again, and sometimes not. I'm not sure why they freeze in the first place. You can see in this video, Shiloh, Addison, and Rowan are all frozen. Each avatar is its own Rive component using useRive, and the player card scoreboard animations are handled with Framer Motion.

But it happens for various unknown reasons, like going to another tab and then coming back.

Has anyone had this issue?

25 replies
4mo ago

We've determined that this is a bug in the React Rive runtime. Our current solution is to call rive.startRendering every 100ms to "unstick" our animations. An intersectionObserver is causing rive.stopRendering to be called, but due to a race condition, startRendering is sometimes not called.

https://github.com/rive-app/rive-react/issues/257

4mo ago

Hi! thanks for reporting the issue. Is it possible that in the uploaded video the issue is not happening? I'm not seeing frozen avatars and I can't find the names "Shiloh, Addison, and Rowan".
In any case, it might be helpful to know how these components are getting reordered.
Are they a list of elements each with a key, where the keys could be pointing to a different content after the reorder for example?
Thanks!

4mo ago

Oops, I uploaded the wrong video. Here is the correct one.

4mo ago

got it, yes the ones that move down seem to be the ones that freeze. How are they being moved? are they absolute positioned?

4mo ago

Thanks so much for replying so quickly!

The elements are indeed in a list that is being reordered and the animation is handled by framer motion. The keys for each item in the list are the players' ids, and the content does not change.

Here's an example with a much slower animation.

Notice how all the avatars in the background stop animating. I believe that rive.stopRendering() is being called on them in the runtime, but the problem is that only some of them receive rive.startRendering() and we end up in a bad state.

Nowhere in the code do we call pause or stop, nor do the onPause or onStop callbacks get fired.

We added a click event to each avatar that calls rive.startRendering, it unfreezes them and returns them to normal. So we know that rive.startRendering solves our problem.

This happens all the time in our app. Not just during this scoreboard animation. It happens when we resize the window, go to another tab and come back, when other players join. Sometimes it just seems to happen sporadically.

4mo ago

I've been trying to reproduce the issue but haven't succeeded yet. Would you be able to create a small sample code where this issue is reproduced?
Looking at the code for our intersection observer, I can't see anything that jumps out immediately.
I'm wondering if framer motion does any type of offscreen rendering, or whether it rewrites elements outside the react lifecycle causing this.

(edited) 4mo ago

I've been able to reproduce it with a Reorder component from framer-motion. It looks like framer-motion either changes the size or moves the component off screen somehow, but when it comes back, the IntersectionObserver is not triggered.

4mo ago

I can also confirm that in Safari and Firefox it works correctly. So this looks like a Chrome bug

4mo ago

Good catch! I'm also unable to reproduce outside Chrome. That's a bummer since our app will predominantly run in a Chromium browser.

Our fix for now is to wrap useRive in our own hook that sets an interval to call startRendering() every 100ms. Obviously not ideal, but better than having our animations freeze.

4mo ago

We're looking for a temporary solution on our side. In the meantime, have you tried listening to framer-motion events? I'm not very familiar with the library, but if there's some "onDrop" event that you can listen to and resume the rive animation immediately, it should work.
I've seen in github that you mention it can trigger for other reasons like resizing the window, or leaving the tab. I haven't been able to reproduce those, but could all those be related to framer-motion components?

4mo ago

I don't think so. Our other examples are of components that are not wrapped in Framer Motion components. It's difficult to reproduce consistently. I'll send a code snippet if I can find a reliable way to repro. Thanks

4mo ago

Hi! We've published a new react version 4.10.0.
It fixes the issue that we've been able to reproduce.
It also adds a "shouldUseIntersectionObserver" parameter to the hook that you can pass as "false" to fully opt-out of the functionality in case it's still happening for you.

4mo ago

Amazing! Thank you! I'll update our package and check it out.

4mo ago

Awesome! let us know how it goes.
And thanks for the help tracking it!

4mo ago

Hey Hernan, I updated to the latest Rive packages, and unfortunately we're still experiencing the issue. I believe it's solved for the case we were able to reproduce. However, we've got a bit of a unique scenario. We're building a Discord Activity, so our app runs in Chromium on Discord. We can fairly consistently reproduce the bug by running the activity in Discord and while it's running if you switch to a text channel and then return to the activity, the Rive animations will be frozen. The problem continues to be resolved by calling startRendering in an interval on each of our animations.

I tried removing the interval and adding shouldUseIntersectionObserver: false, to each of our useRive hooks, and the animations freeze when I go to a text channel and come back.

I don't know how to reproduce this in Chrome directly. Maybe it's a bug in Discord. I just don't understand how stopRendering would be called without the intersectionObserver. Maybe it's not being called at all... All I know is that startRendering solves the issue.

In this video, I have the latest versions of @rive-app/canvas and @rive-app/react-canvas and I've passed shouldUseIntersectionObserver: false, to all useRive calls. When I first start the activity, the characters in the bottom right animate when hovered, and the characters in the top left and top right animate continuously. I check out a few text channels and when I come back all the characters are frozen. The hover animations don't work and the characters at the top have stopped.

4mo ago

Hey, there is one other scenario where we don't draw anymore and it also uses an observer.
https://github.com/rive-app/rive-wasm/blob/master/js/src/rive.ts#L2039
Could you try adding a breakpoint there to see if at some point _hasZeroSize is true?
By the way, is there a link that you can share for us to reproduce the problem?

4mo ago

Oh interesting. That could be it. I'm not sure how to add a breakpoint there. Can't find that js code in the devtools, just the wasm. Tried symlinking to a local version of the package, but changes to the js files don't seem to have an effect.
As for a link to the Discord Activity, I'll see if I can send you something tomorrow.
Thanks for all your help.

4mo ago

lol I'd try to set the breakpoint in Chrome's source files. Perhaps searching for "_hasZeroSize" works?