UNPKG

@stianlarsen/react-fade-effects

Version:

A collection of React components for smooth fade-in animations, starting with FadeWords. Easily add fade effects to your UI elements with customizable options.

166 lines (162 loc) 6.15 kB
"use client"; // src/components/fadedWords.tsx import { motion, stagger, useAnimate, useInView } from "framer-motion"; import { useEffect, useMemo, useRef } from "react"; // #style-inject:#style-inject function styleInject(css, { insertAt } = {}) { if (!css || typeof document === "undefined") return; const head = document.head || document.getElementsByTagName("head")[0]; const style = document.createElement("style"); style.type = "text/css"; if (insertAt === "top") { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } // src/css/fadeEffects.css styleInject(".sl-fade-textEffectWrapper {\n display: inline-block;\n vertical-align: inherit;\n text-align: inherit;\n text-decoration: inherit;\n text-wrap: inherit;\n font-size: inherit;\n font-weight: inherit;\n font-style: inherit;\n font-family: inherit;\n line-height: inherit;\n letter-spacing: inherit;\n color: inherit;\n background-color: inherit;\n border: inherit;\n border-radius: inherit;\n}\n.sl-fade-textEffectWrapper .sl-fade-word {\n display: inline-block;\n opacity: 0;\n color: inherit;\n vertical-align: inherit;\n text-align: inherit;\n text-decoration: inherit;\n text-wrap: inherit;\n font-size: inherit;\n font-weight: inherit;\n font-style: inherit;\n font-family: inherit;\n line-height: inherit;\n letter-spacing: inherit;\n background-color: inherit;\n border: inherit;\n border-radius: inherit;\n will-change:\n transform,\n opacity,\n filter;\n backface-visibility: hidden;\n -webkit-backface-visibility: hidden;\n transform: translateZ(0);\n contain: layout style;\n}\n.sl-fade-renderWordsWrapper {\n display: block;\n text-align: inherit;\n text-decoration: inherit;\n text-wrap: inherit;\n font-size: inherit;\n font-weight: inherit;\n font-style: inherit;\n font-family: inherit;\n line-height: inherit;\n letter-spacing: inherit;\n color: inherit;\n background-color: inherit;\n border: inherit;\n border-radius: inherit;\n}\n"); // src/components/fadedWords.tsx import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var GPU_BLUR_PX = 8; var FadedWords = ({ words, className, filter = true, duration = 1, staggerTime = 0.1, delay = 0.2, variant = "default", scaleSize = void 0, once = true, translateAmount = void 0, splitChar = " ", includeSpaces = true }) => { const [scope, animate] = useAnimate(); const isInViewContainer = useRef(null); const isInView = useInView(isInViewContainer, { once, amount: 0.5 }); const tokensArray = useMemo(() => { if (Array.isArray(words)) { return words.map((word) => ({ value: word, isDelimiter: false })); } let delimiterRegex; if (splitChar instanceof RegExp) { delimiterRegex = splitChar; } else { delimiterRegex = new RegExp(escapeRegex(splitChar), "g"); } return tokenize(words, delimiterRegex); }, [words, splitChar]); const initialStyle = useMemo( () => getInitialStyleObject(variant, filter, scaleSize, translateAmount), [variant, filter, scaleSize, translateAmount] ); useEffect(() => { if (!isInView || !isInViewContainer.current || !scope.current) return; const timeoutId = setTimeout(() => { animate( ".singleWord", { opacity: 1, filter: filter ? "blur(0px)" : "none", transform: "translateY(0px) translateX(0px) scale(1)" }, { duration, delay: stagger(staggerTime), ease: [0.25, 0.1, 0.25, 1] } ); }, delay * 1e3); return () => clearTimeout(timeoutId); }, [isInView, animate, delay, duration, filter, staggerTime]); return /* @__PURE__ */ jsx("div", { ref: isInViewContainer, className: "sl-fade-renderWordsWrapper", children: /* @__PURE__ */ jsx( motion.span, { ref: scope, className: `${className || ""} sl-fade-textEffectWrapper`, children: tokensArray.map((token, idx) => /* @__PURE__ */ jsxs( motion.span, { className: "sl-fade-word singleWord", style: initialStyle, children: [ token.value, includeSpaces && !token.isDelimiter && idx < tokensArray.length - 1 ? /* @__PURE__ */ jsx(Fragment, { children: "\xA0" }) : null ] }, token.value + idx )) } ) }); }; var getInitialStyleObject = (variant, filter, scaleSize, translateAmount) => { let translateY = "0px"; let translateX = "0px"; switch (variant) { case "up": translateY = `${translateAmount !== void 0 ? translateAmount : 15}px`; break; case "down": translateY = `${translateAmount !== void 0 ? -translateAmount : -15}px`; break; case "left": translateX = `${translateAmount !== void 0 ? translateAmount : 15}px`; break; case "right": translateX = `${translateAmount !== void 0 ? -translateAmount : -15}px`; break; } const scale = scaleSize !== void 0 ? scaleSize : 1; return { opacity: 0, filter: filter ? `blur(${GPU_BLUR_PX}px)` : "none", display: "inline-block", transform: `translateY(${translateY}) translateX(${translateX}) scale(${scale})` }; }; var tokenize = (str, delimiterRegex) => { const tokens = []; let lastIndex = 0; const regex = new RegExp(delimiterRegex, "g"); let match; while ((match = regex.exec(str)) !== null) { if (match.index > lastIndex) { tokens.push({ value: str.slice(lastIndex, match.index), isDelimiter: false }); } tokens.push({ value: match[0], isDelimiter: true }); lastIndex = regex.lastIndex; } if (lastIndex < str.length) { tokens.push({ value: str.slice(lastIndex), isDelimiter: false }); } return tokens; }; var escapeRegex = (str) => { return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }; export { FadedWords }; //# sourceMappingURL=index.js.map