Skip to content

RFC: Motion definition & APIs#29958

Merged
layershifter merged 19 commits intomicrosoft:masterfrom
layershifter:docs/motion-rfc
Mar 4, 2024
Merged

RFC: Motion definition & APIs#29958
layershifter merged 19 commits intomicrosoft:masterfrom
layershifter:docs/motion-rfc

Conversation

@layershifter
Copy link
Member

@layershifter layershifter commented Nov 30, 2023

Description

Request for Comments (RFC) proposes the use of the Web Animations API for defining and utilizing motion animations in Fluent UI React components. It discusses various aspects including motion definition, APIs, usage & replacement, performance, and potential issues, while also comparing it with the CSS-based approach.


Preview link (as of 2024-08-15) 🔗

@github-actions github-actions bot added the Type: RFC Request for Feedback label Nov 30, 2023
@fabricteam
Copy link
Collaborator

fabricteam commented Nov 30, 2023

📊 Bundle size report

🤖 This report was generated against 5fb68e9bc86c1cbd66249c639a5565fe7a0b22b4

@codesandbox-ci
Copy link

codesandbox-ci bot commented Nov 30, 2023

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

@size-auditor
Copy link

size-auditor bot commented Nov 30, 2023

Asset size changes

Size Auditor did not detect a change in bundle size for any component!

Baseline commit: 8b932ca5ae309781abbf07ea48eff1d5581f4439 (build)

@marcosmoura
Copy link
Contributor

marcosmoura commented Jan 31, 2024

  • Updates: Included more examples and description about animationevents

Leaving this comment globally here, as there were no mentions about it, and I was unsure where it belongs within this document (or if it should be in this document or in another RFC).

The reason to create a broader solution to handling animations is that useMotion affects the way we re-render components and how to apply animations using it. But the useful part of the hook is to detect animations, no matter how they are applied.

Even though it is an advanced usage, there is a need to detect the state of an animation, with the most common one being for "presence" cases. That was the main reason to create the useMotion hook in the first place.

Some examples:

  • Detecting when something finished appearing or disappearing from the screen to display/hide another UI element.
  • Creating some sort of orchestrated animations, not based on a group of items (or staggered animations) but for separated/isolated UI elements.

Both mentioned examples can be seen on these examples:
Sync animation between components
Detect animation state
These examples are made based on a real-life scenario used by some apps, such as Loop.

Can we think of a way to accommodate that scenario with the new proposed API?

In these cases, performance is important but might not be the top priority as it is a fairly advanced use case.

Please note that animationend/animationstart events can be used for this, but in case an element has multiple animations, the event will be triggered multiple times and with that be hard to detect when animations really finished. Also, those events do not take into consideration the animation-delay, so animationstart only triggers after the delay, so that is not ideal to detect the full duration of the animation.
Example

@layershifter
Copy link
Member Author

The reason to create a broader solution to handling animations is that useMotion affects the way we re-render components and how to apply animations using it. But the useful part of the hook is to detect animations, no matter how they are applied.

Even though it is an advanced usage, there is a need to detect the state of an animation, with the most common one being for "presence" cases. That was the main reason to create the useMotion hook in the first place.

Some examples:

  • Detecting when something finished appearing or disappearing from the screen to display/hide another UI element.
  • Creating some sort of orchestrated animations, not based on a group of items (or staggered animations) but for separated/isolated UI elements.

Both mentioned examples can be seen on these examples: Sync animation between components Detect animation state These examples are made based on a real-life scenario used by some apps, such as Loop.

Please note that animationend/animationstart events can be used for this, but in case an element has multiple animations, the event will be triggered multiple times and with that be hard to detect when animations really finished.

@marcosmoura as we discussed offline, I'm not fully understand the significance of using a state machine in this context. Let me dig into both scenarios we discussed.

Sync animation between components

From a React perspective, useMotion() operates declaratively, responding to state updates:

const motion = useMotion(stateValue);

The example provided initiates three animations simultaneously in a Drawer, content, and footer. Achieving the same outcome is possible using the Web Animations API or any alternative approach, as the animations' trigger is the stateValue. For example:

function App() {
  return (
    <>
      <Drawer open={stateValue} />
      <Content visible={stateValue} />
      <Footer visible={stateValue} />
    </>
  );
}

Detect animation state

As you mentioned, both examples could function without useMotion() since they revolve around animation states, which could be managed using animation callbacks. For example:

function App() {
  const [open, setOpen] = React.useState(false);
  const [isUnmounted, setIsUnmounted] = React.useState(!open);

  return (
    <>
      <InlineDrawer
        onOpenChange={(_, data) => {
          if (data.open) {
            setIsUnmounted(true);
          }
        }}
        onTransitionEnd={() => {
          /* this is equal to `motion.type === 'unmounted'` */
          if (!open) {
            setIsUnmounted(false);
          }
        }}
      />
      <div className={mounted ? "x" : "y"} />
    </>
  );
}

Similar handling could be achieved for motion.type === 'idle'. I understand your concern about potential issues with animation events, so we can expose callbacks that emulate state, like so:

<Collapse onMotionFinish={() => {}} />

Also, those events do not take into consideration the animation-delay, so animationstart only triggers after the delay, so that is not ideal to detect the full duration of the animation.

Regarding your point about animation-delay... It doesn't seem problematic since we're already triggering motions based on state i.e. stateValue.

@layershifter layershifter merged commit 7583dbf into microsoft:master Mar 4, 2024
@layershifter layershifter deleted the docs/motion-rfc branch March 4, 2024 09:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: RFC Request for Feedback

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants