fluentui icon indicating copy to clipboard operation
fluentui copied to clipboard

RFC: Motion definition & APIs

Open layershifter opened this issue 1 year ago • 6 comments
trafficstars

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 🔗

layershifter avatar Nov 30 '23 16:11 layershifter

📊 Bundle size report

🤖 This report was generated against 5fb68e9bc86c1cbd66249c639a5565fe7a0b22b4

fabricteam avatar Nov 30 '23 16:11 fabricteam

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.

codesandbox-ci[bot] avatar Nov 30 '23 16:11 codesandbox-ci[bot]

Asset size changes

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

Baseline commit: 8b932ca5ae309781abbf07ea48eff1d5581f4439 (build)

size-auditor[bot] avatar Nov 30 '23 17:11 size-auditor[bot]

  • 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

marcosmoura avatar Jan 31 '24 19:01 marcosmoura

@ling1726 to review on behalf of teams-prg

miroslavstastny avatar Feb 05 '24 19:02 miroslavstastny

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 avatar Feb 07 '24 11:02 layershifter

A set of follow up issues is created:

  • #30700
  • #30699
  • #30698
  • #30697
  • #30696
  • #30695

layershifter avatar Mar 04 '24 09:03 layershifter