Skip to content

Commit

Permalink
docs: add Entering/Exiting Playground (#6468)
Browse files Browse the repository at this point in the history
<img width="748" alt="image"
src="https://github.com/user-attachments/assets/84092e36-c2a1-42d1-8056-2ec86dbd112b">



https://github.com/user-attachments/assets/4a738620-6791-4b5b-8ad8-b361be2e0867

## Test plan
1. `yarn` && `yarn start`
2. Test if animations behave correctly.
  • Loading branch information
patrycjakalinska authored Sep 20, 2024
1 parent d0c9d7a commit f76ff9c
Show file tree
Hide file tree
Showing 8 changed files with 969 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ sidebar_position: 1

# Entering/Exiting animations

import { useEnteringExitingPlayground } from '@site/src/components/InteractivePlayground';

Entering/Exiting animations let you animate elements when they are added to or removed from the view hierarchy.

Reanimated comes with a bunch of predefined animations you can customize. For more advanced use-cases, you can use [Keyframes](/docs/layout-animations/keyframe-animations) or create your own [custom entering/exiting animations](/docs/layout-animations/custom-animations).

<InteractivePlayground usePlayground={useEnteringExitingPlayground} />

:::info
Spring-based animations are yet to be introduced to the web. Due to that, playground doesn't cover `springify()` options but they can be applied to your animations on iOS and Android platforms.
:::

## Remarks

- We recommend using layout animation builders outside of components or with `useMemo` to ensure the best performance.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ReducedMotionWarning from '../ReducedMotionWarning';
import useClampPlayground from './useClampPlayground';
import useSpringPlayground from './useSpringPlayground';
import useTimingPlayground from './useTimingPlayground';
import useEnteringExitingPlayground from './useEnteringExitingAnimationPlayground';
import useRepeatPlayground from './useRepeatPlayground';
import useInterpolateColorPlayground from './useInterpolateColorPlayground';
import useAnimatedSensorPlayground from './useAnimatedSensorPlayground';
Expand All @@ -32,6 +33,7 @@ export {
useSpringPlayground,
useTimingPlayground,
useRepeatPlayground,
useEnteringExitingPlayground,
useInterpolateColorPlayground,
useAnimatedSensorPlayground,
useDecayPlayground,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, { Dispatch, SetStateAction, useState, useEffect } from 'react';
import { Range, SelectOption, CheckboxOption } from '../..';
import { EnteringExitingConfigProps } from '..';
import SpringControls from './SpringControls';
import EasingControls from './EasingControls';

// TODO: Options related to spring will be uncommented after springify is introduced to web

const Controls = ({
options,
type,
setType,
isMobile,
canNestEasing,
}: {
options: string[];
type: EnteringExitingConfigProps;
setType: Dispatch<SetStateAction<EnteringExitingConfigProps>>;
isMobile: boolean;
canNestEasing: (easing: string) => boolean;
}) => {
const isBounce = type.animation.toString().toLowerCase().includes('bounce');

return (
<>
<SelectOption
label="Animation"
value={type.animation}
onChange={(option) => {
setType((prevState) => ({
...prevState,
animation: option,
}));
}}
options={options}
/>
{/* {!type.isSpringBased && ( */}
<Range
label="Duration (ms)"
min={100}
max={3000}
step={100}
value={type.duration}
onChange={(option) => {
setType((prevState) => ({
...prevState,
duration: option,
}));
}}
/>
{/* )} */}
<Range
label="Delay (ms)"
min={0}
max={3000}
step={100}
value={type.delay}
onChange={(option) => {
setType((prevState) => ({
...prevState,
delay: option,
}));
}}
/>
{/* {!isBounce && (
<CheckboxOption
label="Spring-based"
value={type.isSpringBased}
onChange={(value) =>
setType((prevState) => ({
...prevState,
easing: value ? null : 'inOut',
nestedEasing: value ? null : 'quad',
isSpringBased: value,
}))
}
/>
)} */}
{/* {type.isSpringBased ? (
<SpringControls type={type} setType={setType} />
) : ( */}
<EasingControls
type={type}
setType={setType}
canNestEasing={canNestEasing}
isMobile={isMobile}
/>
{/* )} */}
</>
);
};

export default Controls;
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
import React, { Dispatch, SetStateAction } from 'react';
import { Range, SelectOption } from '../..';
import { Collapsible } from '@docusaurus/theme-common';
import CollapseButton from '@site/src/components/CollapseButton';
import styles from '../styles.module.css';
import { EnteringExitingConfigProps } from '..';

export interface ControlProps {
type: EnteringExitingConfigProps;
setType: Dispatch<SetStateAction<EnteringExitingConfigProps>>;
canNestEasing: (easing: string) => boolean;
isMobile: boolean;
}

const EASING_OPTIONS = [
'back',
'bezier',
'bounce',
'circle',
'cubic',
'ease',
'elastic',
'exp',
'linear',
'poly',
'quad',
'sin',
'steps',
'in',
'inOut',
'out',
];

const NESTED_EASING_OPTIONS = [
'back',
'bezierFn',
'bounce',
'circle',
'cubic',
'ease',
'elastic',
'exp',
'linear',
'poly',
'quad',
'sin',
'steps',
];

const EasingControls = ({
type,
setType,
canNestEasing,
isMobile,
}: {
type: EnteringExitingConfigProps;
setType: Dispatch<SetStateAction<EnteringExitingConfigProps>>;
canNestEasing: (easing: string) => boolean;
isMobile: boolean;
}) => {
return (
<>
<SelectOption
label="Easing"
value={type.easing}
onChange={(option) => {
setType((prevState) => ({
...prevState,
easing: option,
}));
}}
options={EASING_OPTIONS}
/>
{canNestEasing(type.easing) && (
<SelectOption
label="Easing"
value={type.nestedEasing}
onChange={(option) => {
setType((prevState) => ({
...prevState,
nestedEasing: option,
}));
}}
disabled={!canNestEasing(type.easing)}
options={NESTED_EASING_OPTIONS}
/>
)}
{(type.easing === 'back' || type.nestedEasing === 'back') && (
<Range
label="Step to back"
min={0}
max={10}
step={0.1}
value={type.stepToBack}
onChange={(option) => {
setType((prevState) => ({
...prevState,
stepToBack: option,
}));
}}
/>
)}
{(type.easing === 'bezier' ||
(type.nestedEasing === 'bezierFn' && canNestEasing(type.easing))) && (
<>
{!isMobile && (
<CollapseButton
label="Hide controls"
labelCollapsed="Show controls"
collapsed={type.bezierCollapsed}
onCollapse={() =>
setType((prevState) => ({
...prevState,
bezierCollapsed: !prevState.bezierCollapsed,
}))
}
className={styles.collapseButton}
/>
)}
<Collapsible
collapsed={type.bezierCollapsed}
lazy={false}
onCollapseTransitionEnd={(newCollapsed) => {
setType((prevState) => ({
...prevState,
bezierCollapsed: newCollapsed,
}));
}}
/* As range sliders may hide their overflow on the boundaries during the collapse animation,
* we need to shorten the duration of the animation to 1ms (as 0ms will just not show it). */
animation={{ duration: 1 }}>
<Range
label="x1"
min={0}
max={1}
step={0.01}
value={type.x1}
onChange={(option) => {
setType((prevState) => ({
...prevState,
x1: option,
}));
}}
/>
<Range
label="y1"
min={-1}
max={2}
step={0.01}
value={type.y1}
onChange={(option) => {
setType((prevState) => ({
...prevState,
y1: option,
}));
}}
/>
<Range
label="x2"
min={0}
max={1}
step={0.01}
value={type.x2}
onChange={(option) => {
setType((prevState) => ({
...prevState,
x2: option,
}));
}}
/>
<Range
label="y2"
min={-1}
max={2}
step={0.01}
value={type.y2}
onChange={(option) => {
setType((prevState) => ({
...prevState,
y2: option,
}));
}}
/>
</Collapsible>
</>
)}
{(type.easing === 'poly' || type.nestedEasing === 'poly') && (
<Range
label="Power"
min={1}
max={10}
step={1}
value={type.power}
onChange={(option) => {
setType((prevState) => ({
...prevState,
power: option,
}));
}}
/>
)}
{(type.easing === 'elastic' || type.nestedEasing === 'elastic') && (
<Range
label="Bounciness"
min={0}
max={10}
step={0.1}
value={type.bounciness}
onChange={(option) => {
setType((prevState) => ({
...prevState,
bounciness: option,
}));
}}
/>
)}
{(type.easing === 'steps' || type.nestedEasing === 'steps') && (
<>
<Range
label="Steps"
min={1}
max={15}
step={1}
value={type.steps}
onChange={(option) => {
setType((prevState) => ({
...prevState,
steps: option,
}));
}}
/>
<SelectOption
label="Round to next step"
value={String(type.roundToNextStep)}
onChange={(option) => {
setType((prevState) => ({
...prevState,
roundToNextStep: option === 'true',
}));
}}
options={['true', 'false']}
/>
</>
)}
</>
);
};

export default EasingControls;
Loading

0 comments on commit f76ff9c

Please sign in to comment.