rani-motion
Version:
A simple and powerful React animation library with TypeScript support - animated.div style components with accessibility-first approach
846 lines (823 loc) • 31.7 kB
JavaScript
import React, { useState, useRef, useEffect, createContext, useContext } from 'react';
var jsxRuntime = {exports: {}};
var reactJsxRuntime_production = {};
/**
* @license React
* react-jsx-runtime.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredReactJsxRuntime_production;
function requireReactJsxRuntime_production () {
if (hasRequiredReactJsxRuntime_production) return reactJsxRuntime_production;
hasRequiredReactJsxRuntime_production = 1;
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"),
REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
function jsxProd(type, config, maybeKey) {
var key = null;
void 0 !== maybeKey && (key = "" + maybeKey);
void 0 !== config.key && (key = "" + config.key);
if ("key" in config) {
maybeKey = {};
for (var propName in config)
"key" !== propName && (maybeKey[propName] = config[propName]);
} else maybeKey = config;
config = maybeKey.ref;
return {
$$typeof: REACT_ELEMENT_TYPE,
type: type,
key: key,
ref: void 0 !== config ? config : null,
props: maybeKey
};
}
reactJsxRuntime_production.Fragment = REACT_FRAGMENT_TYPE;
reactJsxRuntime_production.jsx = jsxProd;
reactJsxRuntime_production.jsxs = jsxProd;
return reactJsxRuntime_production;
}
var reactJsxRuntime_development = {};
/**
* @license React
* react-jsx-runtime.development.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredReactJsxRuntime_development;
function requireReactJsxRuntime_development () {
if (hasRequiredReactJsxRuntime_development) return reactJsxRuntime_development;
hasRequiredReactJsxRuntime_development = 1;
"production" !== process.env.NODE_ENV &&
(function () {
function getComponentNameFromType(type) {
if (null == type) return null;
if ("function" === typeof type)
return type.$$typeof === REACT_CLIENT_REFERENCE
? null
: type.displayName || type.name || null;
if ("string" === typeof type) return type;
switch (type) {
case REACT_FRAGMENT_TYPE:
return "Fragment";
case REACT_PROFILER_TYPE:
return "Profiler";
case REACT_STRICT_MODE_TYPE:
return "StrictMode";
case REACT_SUSPENSE_TYPE:
return "Suspense";
case REACT_SUSPENSE_LIST_TYPE:
return "SuspenseList";
case REACT_ACTIVITY_TYPE:
return "Activity";
}
if ("object" === typeof type)
switch (
("number" === typeof type.tag &&
console.error(
"Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
),
type.$$typeof)
) {
case REACT_PORTAL_TYPE:
return "Portal";
case REACT_CONTEXT_TYPE:
return (type.displayName || "Context") + ".Provider";
case REACT_CONSUMER_TYPE:
return (type._context.displayName || "Context") + ".Consumer";
case REACT_FORWARD_REF_TYPE:
var innerType = type.render;
type = type.displayName;
type ||
((type = innerType.displayName || innerType.name || ""),
(type = "" !== type ? "ForwardRef(" + type + ")" : "ForwardRef"));
return type;
case REACT_MEMO_TYPE:
return (
(innerType = type.displayName || null),
null !== innerType
? innerType
: getComponentNameFromType(type.type) || "Memo"
);
case REACT_LAZY_TYPE:
innerType = type._payload;
type = type._init;
try {
return getComponentNameFromType(type(innerType));
} catch (x) {}
}
return null;
}
function testStringCoercion(value) {
return "" + value;
}
function checkKeyStringCoercion(value) {
try {
testStringCoercion(value);
var JSCompiler_inline_result = !1;
} catch (e) {
JSCompiler_inline_result = !0;
}
if (JSCompiler_inline_result) {
JSCompiler_inline_result = console;
var JSCompiler_temp_const = JSCompiler_inline_result.error;
var JSCompiler_inline_result$jscomp$0 =
("function" === typeof Symbol &&
Symbol.toStringTag &&
value[Symbol.toStringTag]) ||
value.constructor.name ||
"Object";
JSCompiler_temp_const.call(
JSCompiler_inline_result,
"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
JSCompiler_inline_result$jscomp$0
);
return testStringCoercion(value);
}
}
function getTaskName(type) {
if (type === REACT_FRAGMENT_TYPE) return "<>";
if (
"object" === typeof type &&
null !== type &&
type.$$typeof === REACT_LAZY_TYPE
)
return "<...>";
try {
var name = getComponentNameFromType(type);
return name ? "<" + name + ">" : "<...>";
} catch (x) {
return "<...>";
}
}
function getOwner() {
var dispatcher = ReactSharedInternals.A;
return null === dispatcher ? null : dispatcher.getOwner();
}
function UnknownOwner() {
return Error("react-stack-top-frame");
}
function hasValidKey(config) {
if (hasOwnProperty.call(config, "key")) {
var getter = Object.getOwnPropertyDescriptor(config, "key").get;
if (getter && getter.isReactWarning) return !1;
}
return void 0 !== config.key;
}
function defineKeyPropWarningGetter(props, displayName) {
function warnAboutAccessingKey() {
specialPropKeyWarningShown ||
((specialPropKeyWarningShown = !0),
console.error(
"%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
displayName
));
}
warnAboutAccessingKey.isReactWarning = !0;
Object.defineProperty(props, "key", {
get: warnAboutAccessingKey,
configurable: !0
});
}
function elementRefGetterWithDeprecationWarning() {
var componentName = getComponentNameFromType(this.type);
didWarnAboutElementRef[componentName] ||
((didWarnAboutElementRef[componentName] = !0),
console.error(
"Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
));
componentName = this.props.ref;
return void 0 !== componentName ? componentName : null;
}
function ReactElement(
type,
key,
self,
source,
owner,
props,
debugStack,
debugTask
) {
self = props.ref;
type = {
$$typeof: REACT_ELEMENT_TYPE,
type: type,
key: key,
props: props,
_owner: owner
};
null !== (void 0 !== self ? self : null)
? Object.defineProperty(type, "ref", {
enumerable: !1,
get: elementRefGetterWithDeprecationWarning
})
: Object.defineProperty(type, "ref", { enumerable: !1, value: null });
type._store = {};
Object.defineProperty(type._store, "validated", {
configurable: !1,
enumerable: !1,
writable: !0,
value: 0
});
Object.defineProperty(type, "_debugInfo", {
configurable: !1,
enumerable: !1,
writable: !0,
value: null
});
Object.defineProperty(type, "_debugStack", {
configurable: !1,
enumerable: !1,
writable: !0,
value: debugStack
});
Object.defineProperty(type, "_debugTask", {
configurable: !1,
enumerable: !1,
writable: !0,
value: debugTask
});
Object.freeze && (Object.freeze(type.props), Object.freeze(type));
return type;
}
function jsxDEVImpl(
type,
config,
maybeKey,
isStaticChildren,
source,
self,
debugStack,
debugTask
) {
var children = config.children;
if (void 0 !== children)
if (isStaticChildren)
if (isArrayImpl(children)) {
for (
isStaticChildren = 0;
isStaticChildren < children.length;
isStaticChildren++
)
validateChildKeys(children[isStaticChildren]);
Object.freeze && Object.freeze(children);
} else
console.error(
"React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
);
else validateChildKeys(children);
if (hasOwnProperty.call(config, "key")) {
children = getComponentNameFromType(type);
var keys = Object.keys(config).filter(function (k) {
return "key" !== k;
});
isStaticChildren =
0 < keys.length
? "{key: someKey, " + keys.join(": ..., ") + ": ...}"
: "{key: someKey}";
didWarnAboutKeySpread[children + isStaticChildren] ||
((keys =
0 < keys.length ? "{" + keys.join(": ..., ") + ": ...}" : "{}"),
console.error(
'A props object containing a "key" prop is being spread into JSX:\n let props = %s;\n <%s {...props} />\nReact keys must be passed directly to JSX without using spread:\n let props = %s;\n <%s key={someKey} {...props} />',
isStaticChildren,
children,
keys,
children
),
(didWarnAboutKeySpread[children + isStaticChildren] = !0));
}
children = null;
void 0 !== maybeKey &&
(checkKeyStringCoercion(maybeKey), (children = "" + maybeKey));
hasValidKey(config) &&
(checkKeyStringCoercion(config.key), (children = "" + config.key));
if ("key" in config) {
maybeKey = {};
for (var propName in config)
"key" !== propName && (maybeKey[propName] = config[propName]);
} else maybeKey = config;
children &&
defineKeyPropWarningGetter(
maybeKey,
"function" === typeof type
? type.displayName || type.name || "Unknown"
: type
);
return ReactElement(
type,
children,
self,
source,
getOwner(),
maybeKey,
debugStack,
debugTask
);
}
function validateChildKeys(node) {
"object" === typeof node &&
null !== node &&
node.$$typeof === REACT_ELEMENT_TYPE &&
node._store &&
(node._store.validated = 1);
}
var React$1 = React,
REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"),
REACT_PORTAL_TYPE = Symbol.for("react.portal"),
REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"),
REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"),
REACT_PROFILER_TYPE = Symbol.for("react.profiler");
var REACT_CONSUMER_TYPE = Symbol.for("react.consumer"),
REACT_CONTEXT_TYPE = Symbol.for("react.context"),
REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"),
REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"),
REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"),
REACT_MEMO_TYPE = Symbol.for("react.memo"),
REACT_LAZY_TYPE = Symbol.for("react.lazy"),
REACT_ACTIVITY_TYPE = Symbol.for("react.activity"),
REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"),
ReactSharedInternals =
React$1.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
hasOwnProperty = Object.prototype.hasOwnProperty,
isArrayImpl = Array.isArray,
createTask = console.createTask
? console.createTask
: function () {
return null;
};
React$1 = {
react_stack_bottom_frame: function (callStackForError) {
return callStackForError();
}
};
var specialPropKeyWarningShown;
var didWarnAboutElementRef = {};
var unknownOwnerDebugStack = React$1.react_stack_bottom_frame.bind(
React$1,
UnknownOwner
)();
var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner));
var didWarnAboutKeySpread = {};
reactJsxRuntime_development.Fragment = REACT_FRAGMENT_TYPE;
reactJsxRuntime_development.jsx = function (type, config, maybeKey, source, self) {
var trackActualOwner =
1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;
return jsxDEVImpl(
type,
config,
maybeKey,
!1,
source,
self,
trackActualOwner
? Error("react-stack-top-frame")
: unknownOwnerDebugStack,
trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask
);
};
reactJsxRuntime_development.jsxs = function (type, config, maybeKey, source, self) {
var trackActualOwner =
1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;
return jsxDEVImpl(
type,
config,
maybeKey,
!0,
source,
self,
trackActualOwner
? Error("react-stack-top-frame")
: unknownOwnerDebugStack,
trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask
);
};
})();
return reactJsxRuntime_development;
}
if (process.env.NODE_ENV === 'production') {
jsxRuntime.exports = requireReactJsxRuntime_production();
} else {
jsxRuntime.exports = requireReactJsxRuntime_development();
}
var jsxRuntimeExports = jsxRuntime.exports;
const animationPresets = {
fadeIn: {
initial: { opacity: 0 },
animate: { opacity: 1 }
},
fadeInUp: {
initial: { opacity: 0, transform: 'translateY(30px)' },
animate: { opacity: 1, transform: 'translateY(0)' }
},
fadeInDown: {
initial: { opacity: 0, transform: 'translateY(-30px)' },
animate: { opacity: 1, transform: 'translateY(0)' }
},
fadeInLeft: {
initial: { opacity: 0, transform: 'translateX(-30px)' },
animate: { opacity: 1, transform: 'translateX(0)' }
},
fadeInRight: {
initial: { opacity: 0, transform: 'translateX(30px)' },
animate: { opacity: 1, transform: 'translateX(0)' }
},
slideInUp: {
initial: { transform: 'translateY(100%)' },
animate: { transform: 'translateY(0)' }
},
slideInDown: {
initial: { transform: 'translateY(-100%)' },
animate: { transform: 'translateY(0)' }
},
slideInLeft: {
initial: { transform: 'translateX(-100%)' },
animate: { transform: 'translateX(0)' }
},
slideInRight: {
initial: { transform: 'translateX(100%)' },
animate: { transform: 'translateX(0)' }
},
scaleIn: {
initial: { opacity: 0, transform: 'scale(0.8)' },
animate: { opacity: 1, transform: 'scale(1)' }
},
scaleInUp: {
initial: { opacity: 0, transform: 'scale(0.8) translateY(30px)' },
animate: { opacity: 1, transform: 'scale(1) translateY(0)' }
},
zoomIn: {
initial: { opacity: 0, transform: 'scale(0.3)' },
animate: { opacity: 1, transform: 'scale(1)' }
},
rotateIn: {
initial: { opacity: 0, transform: 'rotate(-180deg) scale(0.8)' },
animate: { opacity: 1, transform: 'rotate(0deg) scale(1)' }
},
rotateInLeft: {
initial: { opacity: 0, transform: 'rotate(-90deg)' },
animate: { opacity: 1, transform: 'rotate(0deg)' }
},
rotateInRight: {
initial: { opacity: 0, transform: 'rotate(90deg)' },
animate: { opacity: 1, transform: 'rotate(0deg)' }
},
bounceIn: {
initial: { opacity: 0, transform: 'scale(0.3)' },
animate: { opacity: 1, transform: 'scale(1)' }
},
bounceInUp: {
initial: { opacity: 0, transform: 'translateY(100px)' },
animate: { opacity: 1, transform: 'translateY(0)' }
},
flipInX: {
initial: { opacity: 0, transform: 'perspective(400px) rotateX(-90deg)' },
animate: { opacity: 1, transform: 'perspective(400px) rotateX(0deg)' }
},
flipInY: {
initial: { opacity: 0, transform: 'perspective(400px) rotateY(-90deg)' },
animate: { opacity: 1, transform: 'perspective(400px) rotateY(0deg)' }
},
lightSpeedIn: {
initial: { opacity: 0, transform: 'translateX(100%) skewX(-30deg)' },
animate: { opacity: 1, transform: 'translateX(0) skewX(0deg)' }
},
rollIn: {
initial: { opacity: 0, transform: 'translateX(-100%) rotate(-120deg)' },
animate: { opacity: 1, transform: 'translateX(0) rotate(0deg)' }
}
};
const hoverPresets = {
scale: {
default: { transform: 'scale(1)' },
hover: { transform: 'scale(1.05)' }
},
scaleDown: {
default: { transform: 'scale(1)' },
hover: { transform: 'scale(0.95)' }
},
lift: {
default: { transform: 'translateY(0)', boxShadow: '0 2px 10px rgba(0,0,0,0.1)' },
hover: { transform: 'translateY(-5px)', boxShadow: '0 10px 25px rgba(0,0,0,0.15)' }
},
rotate: {
default: { transform: 'rotate(0deg)' },
hover: { transform: 'rotate(5deg)' }
},
skew: {
default: { transform: 'skew(0deg)' },
hover: { transform: 'skew(-2deg)' }
},
glow: {
default: { boxShadow: '0 0 0px rgba(59, 130, 246, 0)', transform: 'scale(1)' },
hover: { boxShadow: '0 0 20px rgba(59, 130, 246, 0.5)', transform: 'scale(1.02)' }
},
bounce: {
default: { transform: 'translateY(0) scale(1)' },
hover: { transform: 'translateY(-10px) scale(1.02)' }
},
wobble: {
default: { transform: 'rotate(0deg)' },
hover: { transform: 'rotate(-5deg)' }
}
};
const clickPresets = {
pulse: {
default: { transform: 'scale(1)' },
active: { transform: 'scale(0.95)' }
},
shake: {
default: { transform: 'translateX(0)' },
active: { transform: 'translateX(-2px)' }
},
ripple: {
default: { position: 'relative', overflow: 'hidden' },
active: { position: 'relative', overflow: 'hidden' }
}
};
const useIntersectionObserver = (options = {}) => {
const { threshold = 0.1, rootMargin = '0px', triggerOnce = true } = options;
const [isIntersecting, setIsIntersecting] = useState(false);
const [hasIntersected, setHasIntersected] = useState(false);
const elementRef = useRef(null);
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
const isElementIntersecting = entry.isIntersecting;
if (isElementIntersecting && (!triggerOnce || !hasIntersected)) {
setIsIntersecting(true);
setHasIntersected(true);
}
else if (!triggerOnce && !isElementIntersecting) {
setIsIntersecting(false);
}
}, { threshold, rootMargin });
if (elementRef.current) {
observer.observe(elementRef.current);
}
return () => observer.disconnect();
}, [threshold, rootMargin, triggerOnce, hasIntersected]);
return { elementRef, isIntersecting };
};
const useReducedMotion = () => {
const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
setPrefersReducedMotion(mediaQuery.matches);
const handleChange = (event) => {
setPrefersReducedMotion(event.matches);
};
mediaQuery.addEventListener('change', handleChange);
return () => mediaQuery.removeEventListener('change', handleChange);
}, []);
return prefersReducedMotion;
};
const RaniOnView = ({ children, animation = 'fadeInUp', customAnimation, duration = 600, delay = 0, easing = 'easeOutQuart', triggerOnce = true, threshold = 0.1, className = '', style = {}, ...props }) => {
const [isVisible, setIsVisible] = useState(false);
const prefersReducedMotion = useReducedMotion();
const { elementRef, isIntersecting } = useIntersectionObserver({
threshold,
triggerOnce
});
useEffect(() => {
if (isIntersecting) {
setTimeout(() => {
setIsVisible(true);
}, delay);
}
else if (!triggerOnce) {
setIsVisible(false);
}
}, [isIntersecting, delay, triggerOnce]);
const getAnimationStyle = () => {
const selectedAnimation = customAnimation || animationPresets[animation] || animationPresets.fadeInUp;
const currentStyle = isVisible ? selectedAnimation.animate : selectedAnimation.initial;
// 접근성 고려: 사용자가 모션 감소를 선호하면 애니메이션 최소화
const finalDuration = prefersReducedMotion ? Math.min(duration, 200) : duration;
return {
...currentStyle,
transition: `all ${finalDuration}ms cubic-bezier(0.25, 0.46, 0.45, 0.94)`,
willChange: prefersReducedMotion ? 'opacity' : 'transform, opacity'
};
};
return (jsxRuntimeExports.jsx("div", { ref: elementRef, className: className, style: { ...getAnimationStyle(), ...style }, ...props, children: children }));
};
const RaniHover = ({ children, animation = 'scale', customAnimation, duration = 300, className = '', style = {}, ...props }) => {
const [isHovered, setIsHovered] = useState(false);
const prefersReducedMotion = useReducedMotion();
const getHoverStyle = () => {
const selectedAnimation = customAnimation || hoverPresets[animation] || hoverPresets.scale;
const currentStyle = isHovered ? selectedAnimation.hover : selectedAnimation.default;
// 접근성 고려: 모션 감소 시 애니메이션 최소화
const finalDuration = prefersReducedMotion ? Math.min(duration, 150) : duration;
return {
...currentStyle,
transition: `all ${finalDuration}ms cubic-bezier(0.4, 0, 0.2, 1)`,
cursor: 'pointer'
};
};
return (jsxRuntimeExports.jsx("div", { className: className, style: { ...getHoverStyle(), ...style }, onMouseEnter: () => !prefersReducedMotion && setIsHovered(true), onMouseLeave: () => setIsHovered(false), ...props, children: children }));
};
const RaniClick = ({ children, animation = 'ripple', customAnimation, className = '', style = {}, onClick, ...props }) => {
const [isClicked, setIsClicked] = useState(false);
const elementRef = useRef(null);
const prefersReducedMotion = useReducedMotion();
const handleClick = (e) => {
if (!prefersReducedMotion) {
setIsClicked(true);
if (animation === 'ripple' && elementRef.current) {
const rect = elementRef.current.getBoundingClientRect();
const size = Math.max(rect.width, rect.height);
const x = e.clientX - rect.left - size / 2;
const y = e.clientY - rect.top - size / 2;
const ripple = document.createElement('div');
ripple.style.cssText = `
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.6);
transform: scale(0);
animation: ripple-animation 0.6s linear;
left: ${x}px;
top: ${y}px;
width: ${size}px;
height: ${size}px;
pointer-events: none;
`;
elementRef.current.appendChild(ripple);
setTimeout(() => {
if (ripple.parentNode) {
ripple.parentNode.removeChild(ripple);
}
}, 600);
}
setTimeout(() => setIsClicked(false), 200);
}
if (onClick)
onClick(e);
};
const getClickStyle = () => {
const selectedAnimation = customAnimation || clickPresets[animation] || clickPresets.pulse;
const currentStyle = isClicked ? selectedAnimation.active : selectedAnimation.default;
return {
...currentStyle,
transition: 'transform 0.1s ease',
cursor: 'pointer'
};
};
useEffect(() => {
if (!document.querySelector('#ripple-keyframes')) {
const style = document.createElement('style');
style.id = 'ripple-keyframes';
style.textContent = `
@keyframes ripple-animation {
to {
transform: scale(4);
opacity: 0;
}
}
`;
document.head.appendChild(style);
}
}, []);
return (jsxRuntimeExports.jsx("div", { ref: elementRef, className: className, style: { ...getClickStyle(), ...style }, onClick: handleClick, ...props, children: children }));
};
const RaniStagger = ({ children, staggerDelay = 100, animation = 'fadeInUp', customAnimation, duration = 600, ...props }) => {
return (jsxRuntimeExports.jsx("div", { ...props, children: children.map((child, index) => (jsxRuntimeExports.jsx(RaniOnView, { animation: animation, customAnimation: customAnimation, duration: duration, delay: index * staggerDelay, children: child }, index))) }));
};
const RaniContext = createContext({
globalDuration: 600,
globalEasing: 'easeOutQuart',
globalDelay: 0,
reducedMotion: false
});
const RaniProvider = ({ children, globalDuration = 600, globalEasing = 'easeOutQuart', globalDelay = 0, reducedMotion = false }) => {
const value = {
globalDuration,
globalEasing,
globalDelay,
reducedMotion
};
return (jsxRuntimeExports.jsx(RaniContext.Provider, { value: value, children: children }));
};
const useRaniAnimation = () => {
const context = useContext(RaniContext);
if (!context) {
throw new Error('useRaniAnimation must be used within a RaniProvider');
}
return context;
};
// 애니메이션 로직을 담은 컴포넌트 생성 함수
function createAnimatedElement(tag) {
const Component = React.forwardRef((props, ref) => {
const { animation = 'fadeInUp', customAnimation, duration = 600, delay = 0, easing = 'easeOutQuart', triggerOnce = true, threshold = 0.1, style = {}, children, ...htmlProps } = props;
const [isVisible, setIsVisible] = useState(false);
const prefersReducedMotion = useReducedMotion();
const internalRef = useRef(null);
const { elementRef, isIntersecting } = useIntersectionObserver({
threshold,
triggerOnce
});
useEffect(() => {
if (isIntersecting) {
setTimeout(() => {
setIsVisible(true);
}, delay);
}
else if (!triggerOnce) {
setIsVisible(false);
}
}, [isIntersecting, delay, triggerOnce]);
const getAnimationStyle = () => {
const selectedAnimation = customAnimation || animationPresets[animation] || animationPresets.fadeInUp;
const currentStyle = isVisible ? selectedAnimation.animate : selectedAnimation.initial;
const finalDuration = prefersReducedMotion ? Math.min(duration, 200) : duration;
return {
...currentStyle,
transition: `all ${finalDuration}ms cubic-bezier(0.25, 0.46, 0.45, 0.94)`,
willChange: prefersReducedMotion ? 'opacity' : 'transform, opacity'
};
};
return React.createElement(tag, {
ref: (el) => {
// intersection observer ref 설정
elementRef.current = el;
// 내부 ref 설정
internalRef.current = el;
// 외부 ref 설정
if (ref) {
if (typeof ref === 'function') {
ref(el);
}
else {
ref.current = el;
}
}
},
style: { ...getAnimationStyle(), ...style },
...htmlProps
}, children);
});
Component.displayName = `animated.${String(tag)}`;
return Component;
}
// animated 객체 생성 (타입 체크 우회)
const animated = {
div: createAnimatedElement('div'),
span: createAnimatedElement('span'),
section: createAnimatedElement('section'),
article: createAnimatedElement('article'),
header: createAnimatedElement('header'),
footer: createAnimatedElement('footer'),
main: createAnimatedElement('main'),
nav: createAnimatedElement('nav'),
h1: createAnimatedElement('h1'),
h2: createAnimatedElement('h2'),
h3: createAnimatedElement('h3'),
h4: createAnimatedElement('h4'),
h5: createAnimatedElement('h5'),
h6: createAnimatedElement('h6'),
p: createAnimatedElement('p'),
a: createAnimatedElement('a'),
button: createAnimatedElement('button'),
img: createAnimatedElement('img'),
ul: createAnimatedElement('ul'),
ol: createAnimatedElement('ol'),
li: createAnimatedElement('li')
};
const easingPresets = {
linear: (t) => t,
easeIn: (t) => t * t,
easeOut: (t) => t * (2 - t),
easeInOut: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
easeOutQuart: (t) => 1 - (--t) * t * t * t,
easeInOutCubic: (t) => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
easeOutBounce: (t) => {
if (t < 1 / 2.75) {
return 7.5625 * t * t;
}
else if (t < 2 / 2.75) {
return 7.5625 * (t -= 1.5 / 2.75) * t + 0.75;
}
else if (t < 2.5 / 2.75) {
return 7.5625 * (t -= 2.25 / 2.75) * t + 0.9375;
}
else {
return 7.5625 * (t -= 2.625 / 2.75) * t + 0.984375;
}
},
easeOutElastic: (t) => {
const c4 = (2 * Math.PI) / 3;
return t === 0
? 0
: t === 1
? 1
: Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
}
};
export { RaniClick, RaniHover, RaniOnView, RaniProvider, RaniStagger, animated, animationPresets, easingPresets, useIntersectionObserver, useRaniAnimation, useReducedMotion };
//# sourceMappingURL=index.esm.js.map