Last active
October 2, 2024 14:23
-
-
Save gordonbrander/a48d41f1d8b749234f44629a810c4d68 to your computer and use it in GitHub Desktop.
transition.ts - micro JS CSS transitions helper
This file contains hidden or 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
let slowAnimations = false; | |
/** Set slow animations for debugging */ | |
export const setSlowAnimations = (isSlow: boolean) => { | |
slowAnimations = isSlow; | |
}; | |
/** @returns 10s if slow animations is turned on, otherwise returns `ms` */ | |
export const slowable = (ms: number) => (slowAnimations ? 10000 : ms); | |
export const durationSm = 250; | |
export const durationMd = 350; | |
export const durationLg = 500; | |
/** Create a promise for a timeout */ | |
export const timeout = (ms: number): Promise<void> => | |
new Promise((resolve) => { | |
setTimeout(resolve, ms); | |
}); | |
export type CubicBezier = { | |
x1: number; | |
y1: number; | |
x2: number; | |
y2: number; | |
}; | |
export const cubicBezier = ( | |
x1: number, | |
y1: number, | |
x2: number, | |
y2: number, | |
): CubicBezier => Object.freeze({ x1, y1, x2, y2 }); | |
export const easeOutCubic = cubicBezier(0.215, 0.61, 0.355, 1); | |
export const easeOutExpo = cubicBezier(0.19, 1, 0.22, 1); | |
/** Create a cubic */ | |
export const cubicBezierCss = ({ x1, y1, x2, y2 }: CubicBezier) => | |
`cubic-bezier(${x1}, ${y1}, ${x2}, ${y2})`; | |
export type Transition = { | |
property: string; | |
duration: number; | |
delay: number; | |
easing: string; | |
value: string; | |
}; | |
export const transition = ({ | |
property, | |
value, | |
duration = 0, | |
delay = 0, | |
easing = "ease-out", | |
}: { | |
property: string; | |
value: string; | |
duration?: number; | |
delay?: number; | |
easing?: string; | |
}): Transition => ({ | |
property, | |
value, | |
duration: slowable(duration), | |
easing, | |
delay, | |
}); | |
/** Get full transition duration by finding the longest duration + delay */ | |
export const fullTransitionDuration = ( | |
transitions: Array<Transition>, | |
): number => Math.max(...transitions.map((t) => t.duration + t.delay)); | |
const transitionsToCssRule = (transitions: Array<Transition>) => | |
transitions | |
.map((t) => `${t.property} ${t.duration}ms ${t.easing} ${t.delay}ms`) | |
.join(", "); | |
/** | |
* Set a property on an element with a CSS transition. | |
* Returns a promise that resolves after the transition completes. | |
* The promise is timeout-based rather than `transitionend`-based, | |
* so its guaranteed to resolve even if the transition is interrupted. | |
* After the transition completes, the transition style is removed. The | |
* value style is kept. | |
* @returns A Promise that resolves to the element after the transition | |
* completes. | |
*/ | |
export const setTransitions = async <E extends HTMLElement>( | |
element: E, | |
transitions: Array<Transition>, | |
): Promise<E> => { | |
const fullDuration = fullTransitionDuration(transitions); | |
element.style.transition = transitionsToCssRule(transitions); | |
for (const t of transitions) { | |
element.style.setProperty(t.property, t.value); | |
} | |
await timeout(fullDuration + 0.001); | |
element.style.removeProperty("transition"); | |
return element; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment