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.

1 lines 10.5 kB
{"version":3,"sources":["../src/index.tsx","../src/components/fadedWords.tsx","#style-inject:#style-inject","../src/css/fadeEffects.css"],"sourcesContent":["\"use client\";\nexport * from \"./types\";\nexport * from \"./components/fadedWords\";\n","// components/FadedWords.tsx\n\n\"use client\";\nimport {\n motion,\n MotionStyle,\n stagger,\n useAnimate,\n useInView,\n} from \"framer-motion\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\nimport { FadeWordsProps, FadeWordsVariantType } from \"../types\";\nimport \"../css/fadeEffects.css\";\n\n// GPU-friendly blur amount — 8px is visually similar to 20px\n// but ~4x cheaper to rasterize per element per frame\nconst GPU_BLUR_PX = 8;\n\nexport const FadedWords = ({\n words,\n className,\n filter = true,\n duration = 1,\n staggerTime = 0.1,\n delay = 0.2,\n variant = \"default\",\n scaleSize = undefined,\n once = true,\n translateAmount = undefined,\n splitChar = \" \",\n includeSpaces = true,\n}: FadeWordsProps) => {\n const [scope, animate] = useAnimate<HTMLDivElement>();\n const isInViewContainer = useRef<HTMLDivElement>(null);\n const isInView = useInView(isInViewContainer, {\n once,\n amount: 0.5,\n });\n\n const tokensArray = useMemo(() => {\n if (Array.isArray(words)) {\n return words.map((word) => ({ value: word, isDelimiter: false }));\n }\n\n let delimiterRegex: RegExp;\n if (splitChar instanceof RegExp) {\n delimiterRegex = splitChar;\n } else {\n delimiterRegex = new RegExp(escapeRegex(splitChar), \"g\");\n }\n return tokenize(words, delimiterRegex);\n }, [words, splitChar]);\n\n const initialStyle = useMemo(\n () => getInitialStyleObject(variant, filter, scaleSize, translateAmount),\n [variant, filter, scaleSize, translateAmount]\n );\n\n useEffect(() => {\n if (!isInView || !isInViewContainer.current || !scope.current) return;\n\n const timeoutId = setTimeout(() => {\n animate(\n \".singleWord\",\n {\n opacity: 1,\n filter: filter ? \"blur(0px)\" : \"none\",\n transform: \"translateY(0px) translateX(0px) scale(1)\",\n },\n {\n duration,\n delay: stagger(staggerTime),\n ease: [0.25, 0.1, 0.25, 1],\n }\n );\n }, delay * 1000);\n\n return () => clearTimeout(timeoutId);\n }, [isInView, animate, delay, duration, filter, staggerTime]);\n\n return (\n <div ref={isInViewContainer} className=\"sl-fade-renderWordsWrapper\">\n <motion.span\n ref={scope}\n className={`${className || \"\"} sl-fade-textEffectWrapper`}\n >\n {tokensArray.map((token, idx) => (\n <motion.span\n key={token.value + idx}\n className=\"sl-fade-word singleWord\"\n style={initialStyle}\n >\n {token.value}\n {includeSpaces &&\n !token.isDelimiter &&\n idx < tokensArray.length - 1 ? (\n <>&nbsp;</>\n ) : null}\n </motion.span>\n ))}\n </motion.span>\n </div>\n );\n};\n\nconst getInitialStyleObject = (\n variant: FadeWordsVariantType,\n filter: boolean,\n scaleSize?: number,\n translateAmount?: number\n): MotionStyle => {\n let translateY = \"0px\";\n let translateX = \"0px\";\n\n switch (variant) {\n case \"up\":\n translateY = `${translateAmount !== undefined ? translateAmount : 15}px`;\n break;\n case \"down\":\n translateY = `${translateAmount !== undefined ? -translateAmount : -15}px`;\n break;\n case \"left\":\n translateX = `${translateAmount !== undefined ? translateAmount : 15}px`;\n break;\n case \"right\":\n translateX = `${translateAmount !== undefined ? -translateAmount : -15}px`;\n break;\n }\n\n const scale = scaleSize !== undefined ? scaleSize : 1;\n\n return {\n opacity: 0,\n filter: filter ? `blur(${GPU_BLUR_PX}px)` : \"none\",\n display: \"inline-block\",\n transform: `translateY(${translateY}) translateX(${translateX}) scale(${scale})`,\n };\n};\n\nconst tokenize = (\n str: string,\n delimiterRegex: RegExp\n): Array<{ value: string; isDelimiter: boolean }> => {\n const tokens: Array<{ value: string; isDelimiter: boolean }> = [];\n let lastIndex = 0;\n\n const regex = new RegExp(delimiterRegex, \"g\");\n let match;\n\n while ((match = regex.exec(str)) !== null) {\n if (match.index > lastIndex) {\n tokens.push({\n value: str.slice(lastIndex, match.index),\n isDelimiter: false,\n });\n }\n tokens.push({ value: match[0], isDelimiter: true });\n lastIndex = regex.lastIndex;\n }\n if (lastIndex < str.length) {\n tokens.push({ value: str.slice(lastIndex), isDelimiter: false });\n }\n return tokens;\n};\n\nconst escapeRegex = (str: string) => {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n};\n","\n export default function styleInject(css, { insertAt } = {}) {\n if (!css || typeof document === 'undefined') return\n \n const head = document.head || document.getElementsByTagName('head')[0]\n const style = document.createElement('style')\n style.type = 'text/css'\n \n if (insertAt === 'top') {\n if (head.firstChild) {\n head.insertBefore(style, head.firstChild)\n } else {\n head.appendChild(style)\n }\n } else {\n head.appendChild(style)\n }\n \n if (style.styleSheet) {\n style.styleSheet.cssText = css\n } else {\n style.appendChild(document.createTextNode(css))\n }\n }\n ","import styleInject from '#style-inject';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\")"],"mappings":"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","names":[]}