react-fast-marquee-shadow-dom
Version:
A lightweight React component that utilizes the power of CSS animations to create silky smooth marquees.
1 lines • 15.1 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../src/components/Marquee.tsx"],"sourcesContent":["import React, {\n Fragment,\n useEffect,\n useState,\n useRef,\n useCallback,\n useMemo,\n ReactNode,\n CSSProperties,\n FC,\n Children,\n RefAttributes,\n} from \"react\";\nimport \"./Marquee.scss\";\n\nexport type MarqueeProps = {\n /**\n * @description Inline style for the container div\n * @type {CSSProperties}\n * @default {}\n */\n style?: CSSProperties;\n /**\n * @description Class name to style the container div\n * @type {string}\n * @default \"\"\n */\n className?: string;\n /**\n * @description Whether to automatically fill blank space in the marquee with copies of the children or not\n * @type {boolean}\n * @default false\n */\n autoFill?: boolean;\n /**\n * @description Whether to play or pause the marquee\n * @type {boolean}\n * @default true\n */\n play?: boolean;\n /**\n * @description Whether to pause the marquee when hovered\n * @type {boolean}\n * @default false\n */\n pauseOnHover?: boolean;\n /**\n * @description Whether to pause the marquee when clicked\n * @type {boolean}\n * @default false\n */\n pauseOnClick?: boolean;\n /**\n * @description The direction the marquee is sliding\n * @type {\"left\" | \"right\" | \"up\" | \"down\"}\n * @default \"left\"\n */\n direction?: \"left\" | \"right\" | \"up\" | \"down\";\n /**\n * @description Speed calculated as pixels/second\n * @type {number}\n * @default 50\n */\n speed?: number;\n /**\n * @description Duration to delay the animation after render, in seconds\n * @type {number}\n * @default 0\n */\n delay?: number;\n /**\n * @description The number of times the marquee should loop, 0 is equivalent to infinite\n * @type {number}\n * @default 0\n */\n loop?: number;\n /**\n * @description Whether to show the gradient or not\n * @type {boolean}\n * @default false\n */\n gradient?: boolean;\n /**\n * @description The color of the gradient\n * @type {string}\n * @default \"white\"\n */\n gradientColor?: string;\n /**\n * @description The width of the gradient on either side\n * @type {number | string}\n * @default 200\n */\n gradientWidth?: number | string;\n /**\n * @description A callback for when the marquee finishes scrolling and stops. Only calls if loop is non-zero.\n * @type {() => void}\n * @default null\n */\n onFinish?: () => void;\n /**\n * @description A callback for when the marquee finishes a loop. Does not call if maximum loops are reached (use onFinish instead).\n * @type {() => void}\n * @default null\n */\n onCycleComplete?: () => void;\n /**\n * @description: A callback function that is invoked once the marquee has finished mounting. It can be utilized to recalculate the page size, if necessary.\n * @type {() => void}\n * @default null\n */\n onMount?: () => void;\n /**\n * @description The children rendered inside the marquee\n * @type {ReactNode}\n * @default null\n */\n children?: ReactNode;\n} & RefAttributes<HTMLDivElement>;\n\nfunction Marquee({\n style = {},\n className = \"\",\n autoFill = false,\n play = true,\n pauseOnHover = false,\n pauseOnClick = false,\n direction = \"left\",\n speed = 50,\n delay = 0,\n loop = 0,\n gradient = false,\n gradientColor = \"white\",\n gradientWidth = 200,\n onFinish,\n onCycleComplete,\n onMount,\n children,\n}): FC<MarqueeProps> {\n // React Hooks\n const [containerWidth, setContainerWidth] = useState(0);\n const [marqueeWidth, setMarqueeWidth] = useState(0);\n const [multiplier, setMultiplier] = useState(1);\n const [isMounted, setIsMounted] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const marqueeRef = useRef<HTMLDivElement>(null);\n\n // Calculate width of container and marquee and set multiplier\n const calculateWidth = useCallback(() => {\n if (marqueeRef.current && containerRef.current) {\n const containerRect = containerRef.current.getBoundingClientRect();\n const marqueeRect = marqueeRef.current.getBoundingClientRect();\n let containerWidth = containerRect.width;\n let marqueeWidth = marqueeRect.width;\n\n // Swap width and height if direction is up or down\n if (direction === \"up\" || direction === \"down\") {\n containerWidth = containerRect.height;\n marqueeWidth = marqueeRect.height;\n }\n\n if (autoFill && containerWidth && marqueeWidth) {\n setMultiplier(\n marqueeWidth < containerWidth\n ? Math.ceil(containerWidth / marqueeWidth)\n : 1\n );\n } else {\n setMultiplier(1);\n }\n\n setContainerWidth(containerWidth);\n setMarqueeWidth(marqueeWidth);\n }\n }, [autoFill, containerRef, direction]);\n\n // Calculate width and multiplier on mount and on window resize\n useEffect(() => {\n if (!isMounted) return;\n\n calculateWidth();\n if (marqueeRef.current && containerRef.current) {\n const resizeObserver = new ResizeObserver(() => calculateWidth());\n resizeObserver.observe(containerRef.current);\n resizeObserver.observe(marqueeRef.current);\n return () => {\n if (!resizeObserver) return;\n resizeObserver.disconnect();\n };\n }\n }, [calculateWidth, containerRef, isMounted]);\n\n // Recalculate width when children change\n useEffect(() => {\n calculateWidth();\n }, [calculateWidth, children]);\n\n useEffect(() => {\n setIsMounted(true);\n }, []);\n\n // Runs the onMount callback, if it is a function, when Marquee is mounted.\n useEffect(() => {\n if (typeof onMount === \"function\") {\n onMount();\n }\n }, []);\n\n // Animation duration\n const duration = useMemo(() => {\n if (autoFill) {\n return (marqueeWidth * multiplier) / speed;\n } else {\n return marqueeWidth < containerWidth\n ? containerWidth / speed\n : marqueeWidth / speed;\n }\n }, [autoFill, containerWidth, marqueeWidth, multiplier, speed]);\n\n const containerStyle = useMemo(\n () => ({\n ...style,\n [\"--pause-on-hover\" as string]:\n !play || pauseOnHover ? \"paused\" : \"running\",\n [\"--pause-on-click\" as string]:\n !play || (pauseOnHover && !pauseOnClick) || pauseOnClick\n ? \"paused\"\n : \"running\",\n [\"--width\" as string]:\n direction === \"up\" || direction === \"down\" ? `100vh` : \"100%\",\n [\"--transform\" as string]:\n direction === \"up\"\n ? \"rotate(-90deg)\"\n : direction === \"down\"\n ? \"rotate(90deg)\"\n : \"none\",\n }),\n [style, play, pauseOnHover, pauseOnClick, direction]\n );\n\n const gradientStyle = useMemo(\n () => ({\n [\"--gradient-color\" as string]: gradientColor,\n [\"--gradient-width\" as string]:\n typeof gradientWidth === \"number\"\n ? `${gradientWidth}px`\n : gradientWidth,\n }),\n [gradientColor, gradientWidth]\n );\n\n const marqueeStyle = useMemo(\n () => ({\n [\"--play\" as string]: play ? \"running\" : \"paused\",\n [\"--direction\" as string]: direction === \"left\" ? \"normal\" : \"reverse\",\n [\"--duration\" as string]: `${duration}s`,\n [\"--delay\" as string]: `${delay}s`,\n [\"--iteration-count\" as string]: !!loop ? `${loop}` : \"infinite\",\n [\"--min-width\" as string]: autoFill ? `auto` : \"100%\",\n }),\n [play, direction, duration, delay, loop, autoFill]\n );\n\n const childStyle = useMemo(\n () => ({\n [\"--transform\" as string]:\n direction === \"up\"\n ? \"rotate(90deg)\"\n : direction === \"down\"\n ? \"rotate(-90deg)\"\n : \"none\",\n }),\n [direction]\n );\n\n // Render {multiplier} number of children\n const multiplyChildren = useCallback(\n (multiplier: number) => {\n return [\n ...Array(\n Number.isFinite(multiplier) && multiplier >= 0 ? multiplier : 0\n ),\n ].map((_, i) => (\n <Fragment key={i}>\n {Children.map(children, (child) => {\n return (\n <div style={childStyle} className=\"rfm-child\">\n {child}\n </div>\n );\n })}\n </Fragment>\n ));\n },\n [childStyle, children]\n );\n\n return !isMounted ? null : (\n <div\n ref={containerRef}\n style={containerStyle}\n className={\"rfm-marquee-container \" + className}\n >\n {gradient && <div style={gradientStyle} className=\"rfm-overlay\" />}\n <div\n className=\"rfm-marquee\"\n style={marqueeStyle}\n onAnimationIteration={onCycleComplete}\n onAnimationEnd={onFinish}\n >\n <div className=\"rfm-initial-child-container\" ref={marqueeRef}>\n {Children.map(children, (child) => {\n return (\n <div style={childStyle} className=\"rfm-child\">\n {child}\n </div>\n );\n })}\n </div>\n {multiplyChildren(multiplier - 1)}\n </div>\n <div className=\"rfm-marquee\" style={marqueeStyle}>\n {multiplyChildren(multiplier)}\n </div>\n </div>\n );\n}\n\nexport default Marquee;\n"],"names":["useState","useRef","useCallback","containerWidth","marqueeWidth","useEffect","useMemo","multiplier","React","Fragment","Children"],"mappings":"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"}