summaryrefslogtreecommitdiff
path: root/node_modules/swup/src/modules/awaitAnimations.ts
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2024-08-30 17:21:18 +0800
committerlittleblack111 <littleblack11111@gmail.com>2024-08-30 17:21:18 +0800
commitefbc2ffe6a1ee5ec38a4396305561d6515d0943e (patch)
tree2b4c94f18f2c792dfda813050755d5c0273fae22 /node_modules/swup/src/modules/awaitAnimations.ts
parent43acceb75957c880a5ba5a31aae221394a36d5f6 (diff)
node fixes, updates
Diffstat (limited to 'node_modules/swup/src/modules/awaitAnimations.ts')
-rw-r--r--node_modules/swup/src/modules/awaitAnimations.ts148
1 files changed, 148 insertions, 0 deletions
diff --git a/node_modules/swup/src/modules/awaitAnimations.ts b/node_modules/swup/src/modules/awaitAnimations.ts
new file mode 100644
index 00000000..e4b77e74
--- /dev/null
+++ b/node_modules/swup/src/modules/awaitAnimations.ts
@@ -0,0 +1,148 @@
+import { queryAll } from '../utils.js';
+import type Swup from '../Swup.js';
+import type { Options } from '../Swup.js';
+
+const TRANSITION = 'transition';
+const ANIMATION = 'animation';
+
+type AnimationType = typeof TRANSITION | typeof ANIMATION;
+type AnimationEndEvent = `${AnimationType}end`;
+type AnimationProperty = 'Delay' | 'Duration';
+type AnimationStyleKey = `${AnimationType}${AnimationProperty}` | 'transitionProperty';
+
+export type AnimationDirection = 'in' | 'out';
+
+/**
+ * Return a Promise that resolves when all CSS animations and transitions
+ * are done on the page. Filters by selector or takes elements directly.
+ */
+export async function awaitAnimations(
+ this: Swup,
+ {
+ selector,
+ elements
+ }: {
+ selector: Options['animationSelector'];
+ elements?: NodeListOf<HTMLElement> | HTMLElement[];
+ }
+): Promise<void> {
+ // Allow usage of swup without animations: { animationSelector: false }
+ if (selector === false && !elements) {
+ return;
+ }
+
+ // Allow passing in elements
+ let animatedElements: HTMLElement[] = [];
+ if (elements) {
+ animatedElements = Array.from(elements);
+ } else if (selector) {
+ animatedElements = queryAll(selector, document.body);
+ // Warn if no elements match the selector, but keep things going
+ if (!animatedElements.length) {
+ console.warn(`[swup] No elements found matching animationSelector \`${selector}\``);
+ return;
+ }
+ }
+
+ const awaitedAnimations = animatedElements.map((el) => awaitAnimationsOnElement(el));
+ const hasAnimations = awaitedAnimations.filter(Boolean).length > 0;
+ if (!hasAnimations) {
+ if (selector) {
+ console.warn(
+ `[swup] No CSS animation duration defined on elements matching \`${selector}\``
+ );
+ }
+ return;
+ }
+
+ await Promise.all(awaitedAnimations);
+}
+
+function awaitAnimationsOnElement(element: HTMLElement): Promise<void> | false {
+ const { type, timeout, propCount } = getTransitionInfo(element);
+
+ // Resolve immediately if no transition defined
+ if (!type || !timeout) {
+ return false;
+ }
+
+ return new Promise((resolve) => {
+ const endEvent: AnimationEndEvent = `${type}end`;
+ const startTime = performance.now();
+ let propsTransitioned = 0;
+
+ const end = () => {
+ element.removeEventListener(endEvent, onEnd);
+ resolve();
+ };
+
+ const onEnd = (event: TransitionEvent | AnimationEvent) => {
+ // Skip transitions on child elements
+ if (event.target !== element) {
+ return;
+ }
+
+ // Skip transitions that happened before we started listening
+ const elapsedTime = (performance.now() - startTime) / 1000;
+ if (elapsedTime < event.elapsedTime) {
+ return;
+ }
+
+ // End if all properties have transitioned
+ if (++propsTransitioned >= propCount) {
+ end();
+ }
+ };
+
+ setTimeout(() => {
+ if (propsTransitioned < propCount) {
+ end();
+ }
+ }, timeout + 1);
+
+ element.addEventListener(endEvent, onEnd);
+ });
+}
+
+function getTransitionInfo(element: Element) {
+ const styles = window.getComputedStyle(element);
+
+ const transitionDelays = getStyleProperties(styles, `${TRANSITION}Delay`);
+ const transitionDurations = getStyleProperties(styles, `${TRANSITION}Duration`);
+ const transitionTimeout = calculateTimeout(transitionDelays, transitionDurations);
+
+ const animationDelays = getStyleProperties(styles, `${ANIMATION}Delay`);
+ const animationDurations = getStyleProperties(styles, `${ANIMATION}Duration`);
+ const animationTimeout = calculateTimeout(animationDelays, animationDurations);
+
+ const timeout = Math.max(transitionTimeout, animationTimeout);
+ const type: AnimationType | null =
+ timeout > 0 ? (transitionTimeout > animationTimeout ? TRANSITION : ANIMATION) : null;
+ const propCount = type
+ ? type === TRANSITION
+ ? transitionDurations.length
+ : animationDurations.length
+ : 0;
+
+ return {
+ type,
+ timeout,
+ propCount
+ };
+}
+
+export function getStyleProperties(styles: CSSStyleDeclaration, key: AnimationStyleKey): string[] {
+ return (styles[key] || '').split(', ');
+}
+
+export function calculateTimeout(delays: string[], durations: string[]): number {
+ while (delays.length < durations.length) {
+ delays = delays.concat(delays);
+ }
+
+ return Math.max(...durations.map((duration, i) => toMs(duration) + toMs(delays[i])));
+}
+
+export function toMs(time: string): number {
+ return parseFloat(time) * 1000;
+}