Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: handle cases when Animation.persist() does not exist #33282

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

spmonahan
Copy link
Contributor

Previous Behavior

The Animation.persist() API is in our full support matrix but was added after browsers in our partial support matrix. As no polyfill exists for this feature and it cannot be compiled away this commit explicitly handles the case when the API does not exist.

New Behavior

When .persist() is not available we short-circuit the animation, using the existing "is reduced motion" code path to make the animation effectively instant while still firing all animation events, etc.

The Animation.persist() API is in our full suport matrix but
was added after browsers in our partial support matrix. As no
polyfill exists for this feature and it cannot be compiled away
this commit explicitly handles the case when the API does not exist.

When .persist() is not available we short-circuit the animation, using
the existing "is reduced motion" code path to make the animation
effectively instant while still firing all animation events, etc.
@spmonahan spmonahan requested a review from a team as a code owner November 16, 2024 01:06
@@ -2,6 +2,9 @@ import * as React from 'react';
import type { AnimationHandle, AtomMotion } from '../types';

function useAnimateAtomsInSupportedEnvironment() {
// eslint-disable-next-line @nx/workspace-no-restricted-globals
const SUPPORTS_PERSIST = typeof window !== 'undefined' && typeof window.Animation?.prototype.persist === 'function';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in love with having this here. Ideally it would live in global scope because SUPPORTS_PERSIST will be constant for any given environment.

I moved it here because I could not find a good way to test it with Jest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could move this to a function check in another module and mock that moduile in the tests, but I don't mind keeping it like this ELEMENT_SUPPORTS_WEB_ANIMATIONS is done similarly

Copy link

📊 Bundle size report

Package & Exports Baseline (minified/GZIP) PR Change
react-accordion
Accordion (including children components)
106.728 kB
32.718 kB
106.866 kB
32.756 kB
138 B
38 B
react-components
react-components: Accordion, Button, FluentProvider, Image, Menu, Popover
220.638 kB
63.918 kB
220.776 kB
63.949 kB
138 B
31 B
react-components
react-components: entire library
1.162 MB
291.16 kB
1.163 MB
291.214 kB
138 B
54 B
react-dialog
Dialog (including children components)
100.297 kB
30.076 kB
100.435 kB
30.116 kB
138 B
40 B
react-motion
@fluentui/react-motion - createMotionComponent()
4.303 kB
1.899 kB
4.441 kB
1.942 kB
138 B
43 B
react-motion
@fluentui/react-motion - createPresenceComponent()
5.038 kB
2.229 kB
5.176 kB
2.273 kB
138 B
44 B
react-toast
Toast (including Toaster)
98.338 kB
29.591 kB
98.476 kB
29.633 kB
138 B
42 B
react-tree
FlatTree
145.048 kB
41.691 kB
145.186 kB
41.718 kB
138 B
27 B
react-tree
PersonaFlatTree
145.736 kB
41.798 kB
145.874 kB
41.824 kB
138 B
26 B
react-tree
PersonaTree
141.967 kB
40.616 kB
142.105 kB
40.643 kB
138 B
27 B
react-tree
Tree
141.279 kB
40.523 kB
141.417 kB
40.555 kB
138 B
32 B
Unchanged fixtures
Package & Exports Size (minified/GZIP)
react-components
react-components: Button, FluentProvider & webLightTheme
69.21 kB
20.174 kB
react-components
react-components: FluentProvider & webLightTheme
44.447 kB
14.59 kB
react-motion
@fluentui/react-motion - PresenceGroup
1.714 kB
819 B
react-portal-compat
PortalCompatProvider
8.39 kB
2.64 kB
react-timepicker-compat
TimePicker
107.398 kB
35.772 kB
🤖 This report was generated against e8a616ecbb0694dabcc04c5202583939facf8446

Copy link

Pull request demo site: URL

@@ -0,0 +1,7 @@
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🕵🏾‍♀️ visual regressions to review in the fluentuiv9 Visual Regression Report

Avatar Converged 2 screenshots
Image Name Diff(in Pixels) Image Type
Avatar Converged.badgeMask.chromium.png 6 Changed
Avatar Converged.Badge Mask RTL.chromium.png 2 Changed
Drawer 2 screenshots
Image Name Diff(in Pixels) Image Type
Drawer.Full Overlay Dark Mode.chromium.png 2561 Changed
Drawer.overlay drawer full.chromium.png 3320 Changed

});

animation.persist();
SUPPORTS_PERSIST && animation.persist();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we just create a polyfill for https://developer.mozilla.org/en-US/docs/Web/API/Animation/commitStyles?

        if (SUPPORTS_PERSIST) {
          animation.persist();
        } else {
          const resultKeyframe = keyframes[keyframes.length - 1];
          Object.assign(element.style, resultKeyframe);
        }

This would ensure that we don't have weirdness when multiple animations are composed https://stackblitz.com/edit/typescript-hqxwu6?file=index.ts

@@ -19,10 +22,10 @@ function useAnimateAtomsInSupportedEnvironment() {
fill: 'forwards',

...params,
...(isReducedMotion && { duration: 1 }),
...((isReducedMotion || !SUPPORTS_PERSIST) && { duration: 1 }),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to go through the reduced motion path? the animation will still work without persist - the main consequence is that with for multiple animations that target the same property, the latest animation will replace older ones

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't strictly need to. My thinking is twofold:

  1. It reduces the likelihood of multiple animations targeting the same property. I don't know if this is really true in practice but if you had multiple, delayed animations it seems like it would be.
  2. If your browser doesn't support .persist() you don't really fully support this feature so we just opt you out of it. Using reduced motion ensures the functionality is observably the same (barring the multiple animation situation) as all animation events will fire, etc. This only affects browsers in our partial support matrix so a graceful degradation approach feels fine to me.

Open to changing this but as-is seems best to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants