react-fast-marquee-shadow-dom
Version:
A lightweight React component that utilizes the power of CSS animations to create silky smooth marquees.
159 lines (153 loc) • 5.78 kB
JavaScript
;
var React = require('react');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
function Marquee({
style = {},
className = "",
autoFill = false,
play = true,
pauseOnHover = false,
pauseOnClick = false,
direction = "left",
speed = 50,
delay = 0,
loop = 0,
gradient = false,
gradientColor = "white",
gradientWidth = 200,
onFinish,
onCycleComplete,
onMount,
children
}) {
const [containerWidth, setContainerWidth] = React.useState(0);
const [marqueeWidth, setMarqueeWidth] = React.useState(0);
const [multiplier, setMultiplier] = React.useState(1);
const [isMounted, setIsMounted] = React.useState(false);
const containerRef = React.useRef(null);
const marqueeRef = React.useRef(null);
const calculateWidth = React.useCallback(() => {
if (marqueeRef.current && containerRef.current) {
const containerRect = containerRef.current.getBoundingClientRect();
const marqueeRect = marqueeRef.current.getBoundingClientRect();
let containerWidth2 = containerRect.width;
let marqueeWidth2 = marqueeRect.width;
if (direction === "up" || direction === "down") {
containerWidth2 = containerRect.height;
marqueeWidth2 = marqueeRect.height;
}
if (autoFill && containerWidth2 && marqueeWidth2) {
setMultiplier(
marqueeWidth2 < containerWidth2 ? Math.ceil(containerWidth2 / marqueeWidth2) : 1
);
} else {
setMultiplier(1);
}
setContainerWidth(containerWidth2);
setMarqueeWidth(marqueeWidth2);
}
}, [autoFill, containerRef, direction]);
React.useEffect(() => {
if (!isMounted) return;
calculateWidth();
if (marqueeRef.current && containerRef.current) {
const resizeObserver = new ResizeObserver(() => calculateWidth());
resizeObserver.observe(containerRef.current);
resizeObserver.observe(marqueeRef.current);
return () => {
if (!resizeObserver) return;
resizeObserver.disconnect();
};
}
}, [calculateWidth, containerRef, isMounted]);
React.useEffect(() => {
calculateWidth();
}, [calculateWidth, children]);
React.useEffect(() => {
setIsMounted(true);
}, []);
React.useEffect(() => {
if (typeof onMount === "function") {
onMount();
}
}, []);
const duration = React.useMemo(() => {
if (autoFill) {
return marqueeWidth * multiplier / speed;
} else {
return marqueeWidth < containerWidth ? containerWidth / speed : marqueeWidth / speed;
}
}, [autoFill, containerWidth, marqueeWidth, multiplier, speed]);
const containerStyle = React.useMemo(
() => ({
...style,
["--pause-on-hover"]: !play || pauseOnHover ? "paused" : "running",
["--pause-on-click"]: !play || pauseOnHover && !pauseOnClick || pauseOnClick ? "paused" : "running",
["--width"]: direction === "up" || direction === "down" ? `100vh` : "100%",
["--transform"]: direction === "up" ? "rotate(-90deg)" : direction === "down" ? "rotate(90deg)" : "none"
}),
[style, play, pauseOnHover, pauseOnClick, direction]
);
const gradientStyle = React.useMemo(
() => ({
["--gradient-color"]: gradientColor,
["--gradient-width"]: typeof gradientWidth === "number" ? `${gradientWidth}px` : gradientWidth
}),
[gradientColor, gradientWidth]
);
const marqueeStyle = React.useMemo(
() => ({
["--play"]: play ? "running" : "paused",
["--direction"]: direction === "left" ? "normal" : "reverse",
["--duration"]: `${duration}s`,
["--delay"]: `${delay}s`,
["--iteration-count"]: !!loop ? `${loop}` : "infinite",
["--min-width"]: autoFill ? `auto` : "100%"
}),
[play, direction, duration, delay, loop, autoFill]
);
const childStyle = React.useMemo(
() => ({
["--transform"]: direction === "up" ? "rotate(90deg)" : direction === "down" ? "rotate(-90deg)" : "none"
}),
[direction]
);
const multiplyChildren = React.useCallback(
(multiplier2) => {
return [
...Array(
Number.isFinite(multiplier2) && multiplier2 >= 0 ? multiplier2 : 0
)
].map((_, i) => /* @__PURE__ */ React__default["default"].createElement(React.Fragment, { key: i }, React.Children.map(children, (child) => {
return /* @__PURE__ */ React__default["default"].createElement("div", { style: childStyle, className: "rfm-child" }, child);
})));
},
[childStyle, children]
);
return !isMounted ? null : /* @__PURE__ */ React__default["default"].createElement(
"div",
{
ref: containerRef,
style: containerStyle,
className: "rfm-marquee-container " + className
},
gradient && /* @__PURE__ */ React__default["default"].createElement("div", { style: gradientStyle, className: "rfm-overlay" }),
/* @__PURE__ */ React__default["default"].createElement(
"div",
{
className: "rfm-marquee",
style: marqueeStyle,
onAnimationIteration: onCycleComplete,
onAnimationEnd: onFinish
},
/* @__PURE__ */ React__default["default"].createElement("div", { className: "rfm-initial-child-container", ref: marqueeRef }, React.Children.map(children, (child) => {
return /* @__PURE__ */ React__default["default"].createElement("div", { style: childStyle, className: "rfm-child" }, child);
})),
multiplyChildren(multiplier - 1)
),
/* @__PURE__ */ React__default["default"].createElement("div", { className: "rfm-marquee", style: marqueeStyle }, multiplyChildren(multiplier))
);
}
module.exports = Marquee;
//# sourceMappingURL=index.cjs.map