@jurneyx2/react-lottie-hooks
Version:
π― Simple and powerful React hooks for DotLottie animations with GSAP ScrollTrigger integration and automatic SSR/CSR detection
721 lines (714 loc) β’ 25.3 kB
JavaScript
'use strict';
var react = require('react');
var react$1 = require('@gsap/react');
var ScrollTrigger = require('gsap/ScrollTrigger');
var gsap = require('gsap');
// src/useLottieScrollTrigger.ts
// src/language.ts
var DEBUG_LANGUAGE = {
ko: {
// λ‘λ© κ΄λ ¨
loading: "GSAP \uBC0F ScrollTrigger \uB77C\uC774\uBE0C\uB7EC\uB9AC\uB97C \uB85C\uB4DC\uD558\uB294 \uC911...",
loadSuccess: "GSAP \uB77C\uC774\uBE0C\uB7EC\uB9AC \uB85C\uB4DC \uC644\uB8CC",
loadError: "GSAP \uB77C\uC774\uBE0C\uB7EC\uB9AC \uB85C\uB4DC \uC2E4\uD328:",
loadComplete: "Lottie \uC560\uB2C8\uBA54\uC774\uC158 \uB85C\uB4DC \uC644\uB8CC",
// Lottie μ΄κΈ°ν
lottieInit: "Lottie \uC560\uB2C8\uBA54\uC774\uC158 \uCD08\uAE30\uD654 \uC911...",
lottieSuccess: "Lottie \uC560\uB2C8\uBA54\uC774\uC158 \uCD08\uAE30\uD654 \uC644\uB8CC",
lottieError: "Lottie \uC560\uB2C8\uBA54\uC774\uC158 \uCD08\uAE30\uD654 \uC2E4\uD328:",
// DotLottie μ μ©
dotLottieSet: "DotLottie \uC778\uC2A4\uD134\uC2A4 \uC124\uC815\uB428:",
// ScrollTrigger κ΄λ ¨
scrollTriggerInit: "ScrollTrigger \uC124\uC815 \uC911...",
scrollTriggerStart: "ScrollTrigger \uC2DC\uC791",
scrollTriggerSuccess: "ScrollTrigger \uC124\uC815 \uC644\uB8CC",
scrollTriggerDestroy: "ScrollTrigger \uD574\uC81C\uB428",
// μ€ν¬λ‘€ μ΄λ²€νΈ
scrollEnter: "\uC2A4\uD06C\uB864 \uC601\uC5ED \uC9C4\uC785",
scrollLeave: "\uC2A4\uD06C\uB864 \uC601\uC5ED \uBC97\uC5B4\uB0A8",
scrollEnterBack: "\uC2A4\uD06C\uB864 \uC601\uC5ED \uC7AC\uC9C4\uC785",
scrollLeaveBack: "\uC2A4\uD06C\uB864 \uC601\uC5ED \uC5ED\uBC29\uD5A5 \uBC97\uC5B4\uB0A8",
// μ λλ©μ΄μ
μ μ΄
playSuccess: "\uC560\uB2C8\uBA54\uC774\uC158 \uC7AC\uC0DD \uC131\uACF5",
playError: "\uC560\uB2C8\uBA54\uC774\uC158 \uC7AC\uC0DD \uC2E4\uD328:",
pauseError: "\uC560\uB2C8\uBA54\uC774\uC158 \uC77C\uC2DC\uC815\uC9C0 \uC2E4\uD328:",
// 쑰건 체ν¬
conditionNotMet: "ScrollTrigger \uC0DD\uC131 \uC870\uAC74 \uBBF8\uCDA9\uC871:",
// νκ²½ κ΄λ ¨
domNotReady: "DOM\uC774 \uC900\uBE44\uB418\uC9C0 \uC54A\uC74C. \uB300\uAE30 \uC911...",
ssrDetected: "SSR \uD658\uACBD \uAC10\uC9C0\uB428. \uD074\uB77C\uC774\uC5B8\uD2B8 \uB9C8\uC6B4\uD2B8 \uB300\uAE30\uC911...",
frameworkDetected: (framework) => `${framework} \uD504\uB808\uC784\uC6CC\uD06C \uAC10\uC9C0\uB428`,
safeDefaultsApplied: "SSR \uD504\uB808\uC784\uC6CC\uD06C\uC6A9 \uC548\uC804\uD55C \uAE30\uBCF8\uAC12 \uC801\uC6A9\uB428",
// νλ μμν¬ κ°μ§
frameworkDetectionFailed: "\uD504\uB808\uC784\uC6CC\uD06C \uAC10\uC9C0 \uC2E4\uD328:",
frameworkDetectionUpdated: "\uD504\uB808\uC784\uC6CC\uD06C \uAC10\uC9C0 \uC5C5\uB370\uC774\uD2B8\uB428:",
frameworkDetectionUpdatedAfterDOM: "DOM \uB85C\uB4DC \uD6C4 \uD504\uB808\uC784\uC6CC\uD06C \uAC10\uC9C0 \uC5C5\uB370\uC774\uD2B8\uB428:",
frameworkReDetectionFailed: "\uD504\uB808\uC784\uC6CC\uD06C \uC7AC\uAC10\uC9C0 \uC2E4\uD328:",
frameworkReDetectionAfterDOMFailed: "DOM \uB85C\uB4DC \uD6C4 \uD504\uB808\uC784\uC6CC\uD06C \uC7AC\uAC10\uC9C0 \uC2E4\uD328:",
gsapInitFailed: "GSAP \uB610\uB294 \uD504\uB808\uC784\uC6CC\uD06C \uAC10\uC9C0 \uCD08\uAE30\uD654 \uC2E4\uD328:",
// μ΄κΈ° νλ μμν¬ κ°μ§ (μ΅μμ μμ€)
initialFrameworkDetectionFailed: "\uD504\uB808\uC784\uC6CC\uD06C \uAC10\uC9C0 \uC2E4\uD328:",
initialGSAPInitFailed: "GSAP \uB610\uB294 \uD504\uB808\uC784\uC6CC\uD06C \uAC10\uC9C0 \uCD08\uAE30\uD654 \uC2E4\uD328:",
// SSR νκ²½
ssrEnvironmentMessage: "SSR \uD658\uACBD\uC5D0\uC11C\uB294 ScrollTrigger\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
// DotLottie λ‘λ©
dotLottieNotLoaded: "DotLottie\uAC00 \uC544\uC9C1 \uB85C\uB4DC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.",
// μ΄λ²€νΈ λ‘κ·Έ
dotLottiePlayEvent: "DotLottie \uC7AC\uC0DD \uC774\uBCA4\uD2B8",
dotLottiePauseEvent: "DotLottie \uC77C\uC2DC\uC815\uC9C0 \uC774\uBCA4\uD2B8",
dotLottieStopEvent: "DotLottie \uC815\uC9C0 \uC774\uBCA4\uD2B8",
// μ μ΄ ν¨μ λ‘κ·Έ
playThrottled: "Play \uD638\uCD9C\uC774 throttle\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
pauseThrottled: "Pause \uD638\uCD9C\uC774 throttle\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
stopThrottled: "Stop \uD638\uCD9C\uC774 throttle\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
playExecuted: "Play \uC2E4\uD589\uB428",
pauseExecuted: "Pause \uC2E4\uD589\uB428",
stopExecuted: "Stop \uC2E4\uD589\uB428",
setFrameExecuted: (frame) => `SetFrame \uC2E4\uD589\uB428: ${frame}`,
// μλ¬ λ©μμ§
playExecutionError: "Play \uC2E4\uD589 \uC911 \uC624\uB958:",
pauseExecutionError: "Pause \uC2E4\uD589 \uC911 \uC624\uB958:",
stopExecutionError: "Stop \uC2E4\uD589 \uC911 \uC624\uB958:",
setFrameExecutionError: "SetFrame \uC2E4\uD589 \uC911 \uC624\uB958:",
// GSAP μ λλ©μ΄μ
gsapAnimationExecution: (trigger) => `GSAP \uC560\uB2C8\uBA54\uC774\uC158 \uC2E4\uD589: ${trigger}`
},
en: {
// Loading related
loading: "Loading GSAP and ScrollTrigger libraries...",
loadSuccess: "GSAP libraries loaded successfully",
loadError: "Failed to load GSAP libraries:",
loadComplete: "Lottie animation loaded successfully",
// Lottie initialization
lottieInit: "Initializing Lottie animation...",
lottieSuccess: "Lottie animation initialized successfully",
lottieError: "Failed to initialize Lottie animation:",
// DotLottie specific
dotLottieSet: "DotLottie instance set:",
// ScrollTrigger related
scrollTriggerInit: "Setting up ScrollTrigger...",
scrollTriggerStart: "ScrollTrigger started",
scrollTriggerSuccess: "ScrollTrigger setup complete",
scrollTriggerDestroy: "ScrollTrigger destroyed",
// Scroll events
scrollEnter: "Entered scroll area",
scrollLeave: "Left scroll area",
scrollEnterBack: "Re-entered scroll area",
scrollLeaveBack: "Left scroll area backwards",
// Animation control
playSuccess: "Animation played successfully",
playError: "Failed to play animation:",
pauseError: "Failed to pause animation:",
// 쑰건 체ν¬
conditionNotMet: "ScrollTrigger creation conditions not met:",
// νκ²½ κ΄λ ¨
domNotReady: "DOM not ready. Waiting...",
ssrDetected: "SSR environment detected. Waiting for client mount...",
frameworkDetected: (framework) => `${framework} framework detected`,
safeDefaultsApplied: "Safe defaults applied for SSR framework",
// νλ μμν¬ κ°μ§
frameworkDetectionFailed: "Framework detection failed:",
frameworkDetectionUpdated: "Framework detection updated:",
frameworkDetectionUpdatedAfterDOM: "Framework detection updated after DOM loaded:",
frameworkReDetectionFailed: "Framework re-detection failed:",
frameworkReDetectionAfterDOMFailed: "Framework re-detection after DOM loaded failed:",
gsapInitFailed: "GSAP or framework detection initialization failed:",
// μ΄κΈ° νλ μμν¬ κ°μ§ (μ΅μμ μμ€)
initialFrameworkDetectionFailed: "Framework detection failed:",
initialGSAPInitFailed: "GSAP or framework detection initialization failed:",
// SSR νκ²½
ssrEnvironmentMessage: "ScrollTrigger cannot be used in SSR environment.",
// DotLottie λ‘λ©
dotLottieNotLoaded: "DotLottie is not loaded yet.",
// μ΄λ²€νΈ λ‘κ·Έ
dotLottiePlayEvent: "DotLottie play event",
dotLottiePauseEvent: "DotLottie pause event",
dotLottieStopEvent: "DotLottie stop event",
// μ μ΄ ν¨μ λ‘κ·Έ
playThrottled: "Play call was throttled.",
pauseThrottled: "Pause call was throttled.",
stopThrottled: "Stop call was throttled.",
playExecuted: "Play executed",
pauseExecuted: "Pause executed",
stopExecuted: "Stop executed",
setFrameExecuted: (frame) => `SetFrame executed: ${frame}`,
// μλ¬ λ©μμ§
playExecutionError: "Error during play execution:",
pauseExecutionError: "Error during pause execution:",
stopExecutionError: "Error during stop execution:",
setFrameExecutionError: "Error during setFrame execution:",
// GSAP μ λλ©μ΄μ
gsapAnimationExecution: (trigger) => `GSAP animation execution: ${trigger}`
}
};
var language_default = DEBUG_LANGUAGE;
// src/utils/detectSSRFramework.ts
var detectSSRFramework = () => {
const isClient2 = typeof window !== "undefined";
if (!isClient2) {
return { isNextJS: false, isRemix: false, isSSRFramework: false };
}
try {
const isNextJS = !!window.__NEXT_DATA__;
let isRemix = false;
if (document.readyState !== "loading") {
isRemix = !!(window.__remixContext || window.__remixRouteModules || window.__remixManifest || document.querySelector("script[data-remix]") || document.querySelector("[data-remix-root]"));
}
return {
isNextJS,
isRemix,
isSSRFramework: isNextJS || isRemix
};
} catch (error) {
const tempMsg = language_default["ko"];
console.warn(tempMsg.initialFrameworkDetectionFailed, error);
return { isNextJS: false, isRemix: false, isSSRFramework: false };
}
};
var isClient = typeof window !== "undefined";
// src/useLottieScrollTrigger.ts
var detectedFramework = {
isNextJS: false,
isRemix: false,
isSSRFramework: false
};
if (isClient) {
try {
detectedFramework = detectSSRFramework();
gsap.gsap.registerPlugin(ScrollTrigger.ScrollTrigger);
} catch (error) {
const tempMsg = language_default["ko"];
console.warn(tempMsg.initialGSAPInitFailed, error);
}
}
var { isSSRFramework } = detectedFramework;
var useLottieScrollTrigger = (options = {}) => {
if (!isClient) {
const safeRefs = {
triggerRef: { current: null },
lottieContainerRef: { current: null }
};
return {
...safeRefs,
isMounted: false,
isDOMReady: false,
isClient: false,
isLoaded: false,
handleDotLottieRef: () => {
},
dotLottie: null,
isDotLottieLoaded: false,
play: () => {
},
pause: () => {
},
stop: () => {
},
setFrame: () => {
},
getCurrentFrame: () => 0,
getIsPlaying: () => false,
isPlaying: false,
currentFrame: 0,
isSSRFramework: false
};
}
const {
start = "top center",
end = "bottom 20%",
markers = process.env.NODE_ENV === "development",
pauseOnLoad = true,
debug = false,
debugLanguage = "ko",
strictMode = isSSRFramework,
waitForDOMReady = isSSRFramework,
// Performance optimization options
enableStateTracking = false,
// Default: false (performance first)
frameUpdateThrottle = 100,
onPlayStateChange,
onFrameChange,
// DotLottie callbacks
onEnter,
onLeave,
onEnterBack,
onLeaveBack,
// Common options
gsapAnimations,
scrollTriggerOptions
} = options;
const [isMounted, setIsMounted] = react.useState(false);
const [isDOMReady, setIsDOMReady] = react.useState(false);
const triggerRef = react.useRef(null);
const lottieContainerRef = react.useRef(null);
const [dotLottie, setDotLottie] = react.useState(null);
const [isDotLottieLoaded, setIsDotLottieLoaded] = react.useState(false);
const playStateRef = react.useRef(false);
const frameRef = react.useRef(0);
const [isPlaying, setIsPlaying] = react.useState(false);
const [currentFrame, setCurrentFrame] = react.useState(0);
const lastUpdateTimeRef = react.useRef(0);
const lastPlayTimeRef = react.useRef(0);
const lastPauseTimeRef = react.useRef(0);
const getCurrentFrame = react.useCallback(() => frameRef.current, []);
const getIsPlaying = react.useCallback(() => playStateRef.current, []);
const msg = language_default[debugLanguage] || language_default["ko"];
react.useEffect(() => {
if (isClient) {
setIsMounted(true);
if (waitForDOMReady) {
const checkDOMReady = () => {
if (document.readyState === "complete" || document.readyState === "interactive") {
setIsDOMReady(true);
try {
const redetected = detectSSRFramework();
if (debug && redetected.isSSRFramework !== detectedFramework.isSSRFramework) {
console.log(msg.frameworkDetectionUpdated, redetected);
}
} catch (error) {
if (debug) console.warn(msg.frameworkReDetectionFailed, error);
}
} else {
const handleDOMContentLoaded = () => {
setIsDOMReady(true);
try {
const redetected = detectSSRFramework();
if (debug && redetected.isSSRFramework !== detectedFramework.isSSRFramework) {
console.log(
msg.frameworkDetectionUpdatedAfterDOM,
redetected
);
}
} catch (error) {
if (debug)
console.warn(msg.frameworkReDetectionAfterDOMFailed, error);
}
};
const handleLoad = () => setIsDOMReady(true);
document.addEventListener(
"DOMContentLoaded",
handleDOMContentLoaded
);
window.addEventListener("load", handleLoad);
return () => {
document.removeEventListener(
"DOMContentLoaded",
handleDOMContentLoaded
);
window.removeEventListener("load", handleLoad);
};
}
};
checkDOMReady();
} else {
setIsDOMReady(true);
}
}
}, [waitForDOMReady, debug]);
const handleDotLottieRef = react.useCallback(
(dotLottieInstance) => {
if (dotLottieInstance) {
if (debug) console.log(msg.dotLottieSet, dotLottieInstance);
setDotLottie(dotLottieInstance);
const handleLoad = () => {
if (debug) console.log(msg.loadComplete);
setIsDotLottieLoaded(true);
if (pauseOnLoad) {
setTimeout(() => {
dotLottieInstance.pause();
}, 100);
}
};
const handleLoadError = (error) => {
console.error(msg.loadError, error);
};
const handlePlay = () => {
playStateRef.current = true;
if (enableStateTracking) {
setIsPlaying(true);
}
onPlayStateChange?.(true);
if (debug) console.log(msg.dotLottiePlayEvent);
};
const handlePause = () => {
playStateRef.current = false;
if (enableStateTracking) {
setIsPlaying(false);
}
onPlayStateChange?.(false);
if (debug) console.log(msg.dotLottiePauseEvent);
};
const handleStop = () => {
playStateRef.current = false;
frameRef.current = 0;
if (enableStateTracking) {
setIsPlaying(false);
setCurrentFrame(0);
}
onPlayStateChange?.(false);
onFrameChange?.(0);
if (debug) console.log(msg.dotLottieStopEvent);
};
const handleFrame = (event) => {
const now = Date.now();
const newFrame = Math.round(event.currentFrame || 0);
frameRef.current = newFrame;
if (now - lastUpdateTimeRef.current > frameUpdateThrottle) {
if (enableStateTracking) {
setCurrentFrame(newFrame);
}
onFrameChange?.(newFrame);
lastUpdateTimeRef.current = now;
}
};
dotLottieInstance.addEventListener("load", handleLoad);
dotLottieInstance.addEventListener("loadError", handleLoadError);
dotLottieInstance.addEventListener("play", handlePlay);
dotLottieInstance.addEventListener("pause", handlePause);
dotLottieInstance.addEventListener("stop", handleStop);
if (debug || enableStateTracking || onFrameChange) {
dotLottieInstance.addEventListener("frame", handleFrame);
}
if (dotLottieInstance.isLoaded) {
handleLoad();
}
const initialPlaying = dotLottieInstance.isPlaying || false;
const initialFrame = Math.round(dotLottieInstance.currentFrame || 0);
playStateRef.current = initialPlaying;
frameRef.current = initialFrame;
if (enableStateTracking) {
setIsPlaying(initialPlaying);
setCurrentFrame(initialFrame);
}
return () => {
dotLottieInstance.removeEventListener("load", handleLoad);
dotLottieInstance.removeEventListener("loadError", handleLoadError);
dotLottieInstance.removeEventListener("play", handlePlay);
dotLottieInstance.removeEventListener("pause", handlePause);
dotLottieInstance.removeEventListener("stop", handleStop);
if (debug || enableStateTracking || onFrameChange) {
dotLottieInstance.removeEventListener("frame", handleFrame);
}
};
} else {
setDotLottie(null);
setIsDotLottieLoaded(false);
setIsPlaying(false);
setCurrentFrame(0);
}
},
[debug, pauseOnLoad, msg]
);
const executeGsapAnimation = react.useCallback(
(trigger) => {
if (!isClient || !gsapAnimations || !lottieContainerRef.current) return;
const {
rotation = 0,
scale = 1,
x = 0,
y = 0,
opacity = 1,
duration = 1,
ease = "power2.out",
trigger: animationTrigger = "enter"
} = gsapAnimations;
if (animationTrigger === trigger) {
if (debug)
console.log(msg.gsapAnimationExecution(trigger), gsapAnimations);
gsap.gsap.to(lottieContainerRef.current, {
rotation,
scale,
x,
y,
opacity,
duration,
ease
});
}
},
[gsapAnimations, debug]
);
const defaultOnEnter = react.useCallback(
(dotLottie2) => {
if (debug) console.log(msg.scrollEnter);
try {
dotLottie2.setFrame(0);
dotLottie2.play();
if (debug) console.log(msg.playSuccess);
} catch (error) {
console.error(msg.playError, error);
}
},
[debug, msg]
);
const defaultOnLeave = react.useCallback(
(dotLottie2) => {
if (debug) console.log(msg.scrollLeave);
try {
dotLottie2.pause();
} catch (error) {
console.error(msg.pauseError, error);
}
},
[debug, msg]
);
const defaultOnEnterBack = react.useCallback(
(dotLottie2) => {
if (debug) console.log(msg.scrollEnterBack);
try {
dotLottie2.play();
} catch (error) {
console.error(msg.playError, error);
}
},
[debug, msg]
);
const defaultOnLeaveBack = react.useCallback(
(dotLottie2) => {
if (debug) console.log(msg.scrollLeaveBack);
try {
dotLottie2.pause();
} catch (error) {
console.error(msg.pauseError, error);
}
},
[debug, msg]
);
const createScrollTriggerHandlers = react.useCallback(() => {
if (dotLottie) {
return {
onEnter: () => {
const handler = onEnter || defaultOnEnter;
handler(dotLottie);
executeGsapAnimation("enter");
},
onLeave: () => {
const handler = onLeave || defaultOnLeave;
handler(dotLottie);
executeGsapAnimation("leave");
},
onEnterBack: () => {
const handler = onEnterBack || defaultOnEnterBack;
handler(dotLottie);
executeGsapAnimation("enterBack");
},
onLeaveBack: () => {
const handler = onLeaveBack || defaultOnLeaveBack;
handler(dotLottie);
executeGsapAnimation("leaveBack");
}
};
}
return {};
}, [
dotLottie,
onEnter,
onLeave,
onEnterBack,
onLeaveBack,
defaultOnEnter,
defaultOnLeave,
defaultOnEnterBack,
defaultOnLeaveBack,
executeGsapAnimation
]);
react$1.useGSAP(() => {
if (!isClient) {
if (debug) console.log(msg.ssrEnvironmentMessage);
return;
}
const isAnimationReady = dotLottie && isDotLottieLoaded;
const basicConditions = isMounted && isAnimationReady && triggerRef.current;
const strictConditions = strictMode ? isDOMReady && document.readyState === "complete" : true;
if (!basicConditions || !strictConditions) {
if (debug) {
console.log(msg.conditionNotMet, {
isMounted,
isAnimationReady,
triggerRef: !!triggerRef.current,
...strictMode && {
isDOMReady,
documentReady: document.readyState === "complete"
}
});
}
return;
}
if (debug) console.log(msg.scrollTriggerStart);
if (ScrollTrigger.ScrollTrigger) {
ScrollTrigger.ScrollTrigger.refresh();
}
const handlers = createScrollTriggerHandlers();
const scrollTriggerConfig = {
trigger: triggerRef.current,
start,
end,
markers,
...handlers,
...scrollTriggerOptions || {}
};
if (gsapAnimations?.scrub !== void 0) {
scrollTriggerConfig.scrub = gsapAnimations.scrub;
}
const trigger = ScrollTrigger.ScrollTrigger.create(scrollTriggerConfig);
return () => {
trigger.kill();
if (debug) console.log(msg.scrollTriggerDestroy);
};
}, [
isMounted,
dotLottie,
isDotLottieLoaded,
isDOMReady,
strictMode,
start,
end,
markers,
createScrollTriggerHandlers,
debug,
msg,
scrollTriggerOptions,
gsapAnimations
]);
const play = react.useCallback(() => {
if (!dotLottie || !isDotLottieLoaded) {
if (debug) console.warn(msg.dotLottieNotLoaded);
return;
}
const now = Date.now();
if (now - lastPlayTimeRef.current < 100) {
if (debug) console.log(msg.playThrottled);
return;
}
lastPlayTimeRef.current = now;
try {
dotLottie.play();
playStateRef.current = true;
if (enableStateTracking) {
setIsPlaying(true);
}
onPlayStateChange?.(true);
if (debug) console.log(msg.playExecuted);
} catch (error) {
console.error(msg.playExecutionError, error);
}
}, [
dotLottie,
isDotLottieLoaded,
enableStateTracking,
onPlayStateChange,
debug
]);
const pause = react.useCallback(() => {
if (!dotLottie || !isDotLottieLoaded) {
if (debug) console.warn(msg.dotLottieNotLoaded);
return;
}
const now = Date.now();
if (now - lastPauseTimeRef.current < 100) {
if (debug) console.log(msg.pauseThrottled);
return;
}
lastPauseTimeRef.current = now;
try {
dotLottie.pause();
playStateRef.current = false;
if (enableStateTracking) {
setIsPlaying(false);
}
onPlayStateChange?.(false);
if (debug) console.log(msg.pauseExecuted);
} catch (error) {
console.error(msg.pauseExecutionError, error);
}
}, [
dotLottie,
isDotLottieLoaded,
enableStateTracking,
onPlayStateChange,
debug
]);
const stop = react.useCallback(() => {
if (!dotLottie || !isDotLottieLoaded) {
if (debug) console.warn(msg.dotLottieNotLoaded);
return;
}
const now = Date.now();
if (now - lastPauseTimeRef.current < 100) {
if (debug) console.log(msg.stopThrottled);
return;
}
lastPauseTimeRef.current = now;
try {
dotLottie.stop();
playStateRef.current = false;
frameRef.current = 0;
if (enableStateTracking) {
setIsPlaying(false);
setCurrentFrame(0);
}
onPlayStateChange?.(false);
onFrameChange?.(0);
if (debug) console.log(msg.stopExecuted);
} catch (error) {
console.error(msg.stopExecutionError, error);
}
}, [
dotLottie,
isDotLottieLoaded,
enableStateTracking,
onPlayStateChange,
onFrameChange,
debug
]);
const setFrame = react.useCallback(
(frame) => {
if (!dotLottie || !isDotLottieLoaded) {
if (debug) console.warn(msg.dotLottieNotLoaded);
return;
}
try {
dotLottie.setFrame(frame);
frameRef.current = frame;
if (enableStateTracking) {
setCurrentFrame(frame);
}
onFrameChange?.(frame);
if (debug) console.log(msg.setFrameExecuted(frame));
} catch (error) {
console.error(msg.setFrameExecutionError, error);
}
},
[dotLottie, isDotLottieLoaded, enableStateTracking, onFrameChange, debug]
);
const isLoaded = isDotLottieLoaded;
return {
// Common refs and states
triggerRef,
lottieContainerRef,
// Used for GSAP animations
isMounted,
isDOMReady,
isClient,
isLoaded,
// DotLottie specific
handleDotLottieRef,
// Used in DotLottieReact
dotLottie,
isDotLottieLoaded,
// Common control functions
play,
pause,
stop,
setFrame,
// Ref-based getters (no re-rendering)
getCurrentFrame,
getIsPlaying,
// React state based (updated only when enableStateTracking is true)
isPlaying,
currentFrame,
// Environment info
isSSRFramework
};
};
exports.useLottieScrollTrigger = useLottieScrollTrigger;