Created
October 28, 2019 08:59
-
-
Save ShayDavidson/da2fdc93d9512eef3e77df1e505d479a to your computer and use it in GitHub Desktop.
UseAnimation hook
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useMemo } from 'react'; | |
import { easing } from 'ts-easing'; | |
import { useTween } from 'react-use'; | |
import { clampedInverseLerp, lerp } from '../utils/math'; | |
export type EasingName = keyof typeof easing; | |
export interface Animation { | |
from: number; | |
to: number; | |
duration: number; | |
delay: number; | |
easing: EasingName; | |
} | |
export interface PropertyAnimation { | |
property: string; | |
animations: Animation[]; | |
} | |
export interface PropertiesAnimationsValues { | |
[prop: string]: number; | |
} | |
export interface AnimationOptions { | |
reversed?: boolean; | |
} | |
export default function useAnimation( | |
propertyAnimations: PropertyAnimation[], | |
{ reversed }: AnimationOptions = {} | |
): PropertiesAnimationsValues { | |
const totalDuration = useMemo( | |
() => | |
Math.max( | |
...propertyAnimations.flatMap(propertyAnimation => | |
propertyAnimation.animations.map(animation => animation.delay + animation.duration) | |
) | |
), | |
[propertyAnimations] | |
); | |
let t = useTween('linear', totalDuration); | |
if (reversed) t = 1 - t; | |
return propertyAnimations.reduce<PropertiesAnimationsValues>((result, propertyAnimation) => { | |
const animationsTimes = propertyAnimation.animations.map(animation => { | |
const fromT = totalDuration === 0 ? 0 : animation.delay / totalDuration; | |
const toT = totalDuration === 0 ? 0 : (animation.delay + animation.duration) / totalDuration; | |
const relativeT = clampedInverseLerp(fromT, toT, t); | |
return { fromT, toT, relativeT, animation }; | |
}); | |
// this piece of code relies on the animations to be ordered by their time. | |
let matchingAnimationTime = animationsTimes[0]; | |
for (let i = 0; i < animationsTimes.length; i++) { | |
const { toT } = animationsTimes[i]; | |
matchingAnimationTime = animationsTimes[i]; | |
if (t <= toT) { | |
// matching the animation time frame (or before it), no longer need to look. | |
break; | |
} | |
} | |
const { animation: matchingAnimation, relativeT } = matchingAnimationTime; | |
const value = lerp(matchingAnimation.from, matchingAnimation.to, easing[matchingAnimation.easing](relativeT)); | |
return { ...result, [propertyAnimation.property]: value }; | |
}, {}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment