- Status: Proposed
- Deciders: Abyss Mobile Team
- Date: 05/22/2024
Context
Many Abyss components use animations and motion to build user expectation and give feedback. Currently the interactions are built into each component using Animated from React Native. Abyss Design has standardized animation interactions for the library and we look to do the same in development.
Decision
Create a useAnimation hook to standardize the animation types for Abyss components and simplify additional customization. The hook returns animate, value, interpolations, as well as Animated functions used throughout Abyss.
export const useAnimation = (value: number, config: AnimationConfig) => { ... return { animate, value: animatedValue, interpolations, createAnimation, add, subtract, multiply, divide, delay, parallel, loop, sequence, };};const { animate, value, interpolations } = useAnimation(1, { easing: 'gentle', interpolations: { background: { inputRange: [0.95, 1], outputRange: ['$primary1', '$info1'], tokenType: 'colors', }, },});
...
<Button onPressIn={()=> {animate(0.95)}} onPressOut={()=> {animate(1)}} ref={ref} {...abyssProps('button-root', { style: { transform: [{ scale: value }], backgroundColor: interpolations.background, }, })}>Press Me </Button>Animate
The animate function requires a toValue be passed in, with the option for overrides and a callback function.
const animate = ( toValue: number, configOverride?: Partial<AnimationConfigType>, callback?: Animated.EndCallback) => { return createAnimation(toValue, configOverride).start(callback);};The onPressIn and onPressOut or handler functions would then be able to adjust the toValue by accepting a number.
<Button onPressIn={()=> {animate(0.50)}} onPressOut={()=> {animate(1)}}>Overrides
By default the animation type is set to 'timing'. Additional types supported are 'spring' and 'instant' with the following animation config open to override:
interface AnimationConfig extends BaseAnimationConfig { type?: 'spring' | 'timing' | 'instant'; interpolations?: Record<string, InterpolationConfigType>; delay?: number | undefined; // timing easing?: Easing; duration?: number | undefined; // spring overshootClamping?: boolean | undefined; restDisplacementThreshold?: number | undefined; restSpeedThreshold?: number | undefined; velocity?: number | { x: number, y: number } | undefined; bounciness?: number | undefined; speed?: number | undefined; tension?: number | undefined; friction?: number | undefined; stiffness?: number | undefined; mass?: number | undefined; damping?: number | undefined;}Interpolations
The interpolations prop would be able to take in multiple interpolation objects to be used on the same animated value.
const { value, animate, interpolations } = useAnimation(1, { easing: 'gentle', interpolations:{ backgroundColor: { inputRange: [0.95, 1], outputRange: ['$info1', '$primary1'] tokenType: 'colors', }, opacity: { inputRange: [0.95, 1], outputRange: [0, 1] }, } });Additional Functionality
Other Animated methods currently used throughout Abyss components can also be returned.
const parallel = ( animations: Animated.CompositeAnimation[], parallelConfig?: Animated.ParallelConfig) => { return Animated.parallel(animations, parallelConfig);};
const loop = ( animation: Animated.CompositeAnimation, loopConfig?: Animated.LoopAnimationConfig) => { return Animated.loop(animation, loopConfig);};
const sequence = (animations: Animated.CompositeAnimation[]) => { return Animated.sequence(animations);};
const delay = (time: number) => { return Animated.delay(time);};
const add = (num: number) => { return Animated.add(animatedValue, num);};
const subtract = (num: number) => { return Animated.subtract(animatedValue, num);};
const divide = (num: number) => { return Animated.divide(animatedValue, num);};
const multiply = (num: number) => { return Animated.multiply(animatedValue, num);};Alternatives Considered
- Continue with current implementation of animations that involves importing Animated within each component that uses motion.
- Lots of imports and duplicate code within the component library
- Alternatively this would be not spending the time updating existing components
- Not as customizable for consuming teams
- Would be consumers responsibility to add the correct animations to components that do not already come animated by Abyss
Consequences
Positive:
- Less imports
- All imports will be inside the hook
- All logic will be standardized
- Will allow for customizations when necessary
Negative:
- Updating existing components will take a large amount of time
- Any future updates to the hook will affect all the components using it which may lead to a greater number of defects should anything go wrong with a change
Future Considerations
- Implementation will be delivered in US6655564.
References
Revision History
- 01/23/2024: Story updated with ADR requirement.
- 05/28/2024: Updated with code example.
- 05/29/2024: Working code updated.
- 06/03/2024: Link to draft PR added.
- 06/05/2024: Code updated to use Typescript.
- 06/10/2024: Code updated
- 06/17/2024: ADR updated