|
| 1 | +# Shared Animation Backend |
| 2 | + |
| 3 | +[🏠 Home](../../../../../../../__docs__/README.md) |
| 4 | + |
| 5 | +Shared Animation Backend is a part of the React Native renderer that enables |
| 6 | +animation frameworks to update props of React components without going through |
| 7 | +React's JavaScript rendering pipeline. |
| 8 | + |
| 9 | +Animation Backend allows for updates of both layout and non-layout props. If |
| 10 | +there are no layout updates, the animations will go through the |
| 11 | +`synchronouslyUpdateProps` path, otherwise a Fabric commit will be performed. To |
| 12 | +synchronize the changes with React, we use the `AnimationBackendCommitHook`. |
| 13 | + |
| 14 | +## 🚀 Usage |
| 15 | + |
| 16 | +The backend is not meant to be used directly. Its main purpose is to serve as a |
| 17 | +layer over the Fabric renderer that animation frameworks (like Animated or |
| 18 | +Reanimated) can use. A framework that wants to make use of the backend has to |
| 19 | +register a callback with the `start` method. The callback will be executed on |
| 20 | +each animation frame (supported by native mechanisms like |
| 21 | +DisplayLink/Choreographer) and should return a list of prop updates that should |
| 22 | +be applied for the given timestamp. To stop receiving callbacks the framework |
| 23 | +should call `stop` with the appropriate callbackID. For updates that need to be |
| 24 | +applied synchronously (e.g. gesture events), one can use the `trigger` method. |
| 25 | + |
| 26 | +## 📐 Design |
| 27 | + |
| 28 | + |
| 29 | + |
| 30 | +The main purpose of the Animation Backend is to bring long-term stability to |
| 31 | +animation solutions. The New Architecture was designed to maintain React props |
| 32 | +in check with the `ShadowTree`. This poses a problem for any scenario when we |
| 33 | +want to apply updates to the `ShadowTree` without going through the React |
| 34 | +rendering pipeline, which is exactly what animations want to do. Any commit that |
| 35 | +happens outside of React on a different thread will be overwritten by following |
| 36 | +React updates, as the new `ShadowTree` revision created by React is based on the |
| 37 | +revision that is currently held by React. To mitigate that issue, we use the |
| 38 | +`AnimationBackendCommitHook` that runs on every React commit and fixes |
| 39 | +overridden props. In this section, we give a more detailed description of the |
| 40 | +components that make up the Shared Animation Backend. |
| 41 | + |
| 42 | +### AnimationBackend |
| 43 | + |
| 44 | +This is the heart of the backend. This component is responsible for managing |
| 45 | +user-provided callbacks, segregating props, and applying them either through the |
| 46 | +`synchronouslyUpdateProps` path (when there are no layout updates), or through a |
| 47 | +Fabric commit performed on the main thread. This component is exposed through |
| 48 | +`UIManager` with the `UIManagerAnimationBackend` interface. |
| 49 | + |
| 50 | +### AnimatedProps |
| 51 | + |
| 52 | +This is the structure that should be returned by the callback registered with |
| 53 | +the `start` method. It represents updates that are to be applied for a single |
| 54 | +component identified by `ShadowNodeFamily` in `AnimationMutation`. You shouldn't |
| 55 | +interact with this class directly, it's better to use it through the |
| 56 | +`PropsBuilder`. `AnimatedProps` support the `RawProps` format, but they also can |
| 57 | +hold a vector of `AnimatedProp` (a new C++ structure) for a select subset of the |
| 58 | +usual `ViewProps`. These can be applied to the cloned props in a more efficient |
| 59 | +way. |
| 60 | + |
| 61 | +### AnimationBackendCommitHook |
| 62 | + |
| 63 | +This component is responsible for keeping animation updates synchronized with |
| 64 | +React updates. In Fabric, when React performs a commit, it disregards the |
| 65 | +currently mounted revision of the `ShadowTree`. Instead, it bases the new |
| 66 | +revision on the `ShadowNodes` it holds. These nodes do not represent any prop |
| 67 | +updates that happened when the Animation Backend made a commit on the main |
| 68 | +thread (or applied non-layout props through the fast-path). This would create an |
| 69 | +issue where a rerender that happens during the animation would display a wrong |
| 70 | +state for a split second. To fix this, we also write animation updates to |
| 71 | +`AnimatedPropsRegistry`. When React performs a commit, the |
| 72 | +`AnimationBackendCommitHook` is able to overwrite any stale props by using the |
| 73 | +value from the registry. Since the commit hook runs on React commits, it is safe |
| 74 | +for it to use RSNRU to propagate these updates to React. After the commit hook |
| 75 | +runs, we clear the registry. If the prop is not explicitly changed by the user |
| 76 | +in a render, it will remain unchanged by React on subsequent rerenders. |
| 77 | + |
| 78 | +Additionally, in the callback registered in `start`, one can return a list of |
| 79 | +surfaces that should perform a commit on the JS thread, to have the RSNRU |
| 80 | +propagate the updates to React. This was used by Animated before the backend was |
| 81 | +created. |
| 82 | + |
| 83 | +### AnimationChoreographer |
| 84 | + |
| 85 | +This is an abstraction layer that wraps the native frame callback mechanisms. On |
| 86 | +iOS, it has a corresponding `RCTDisplayLink`, and on Android, it uses the |
| 87 | +`Choreographer`. For any new platform that wants to adopt the Animation Backend, |
| 88 | +this is the part that needs to be implemented. |
0 commit comments