diff --git a/example/src/App.tsx b/example/src/App.tsx index 94a0399..c4d1fe6 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -2,11 +2,12 @@ import Ticker from 'framer-motion-ticker'; import React from 'react'; function App() { + const [isPlaying, setIsPlaying] = React.useState(true) const colors = ['#632bf3', '#f122c8', '#f16022', '#9ef344', '#44d3f3']; return (
- + setIsPlaying(false)} onMouseLeave={() => setIsPlaying(true)} isPlaying={isPlaying}> {colors.map((item, index) => (
", "license": "MIT", diff --git a/src/index.tsx b/src/index.tsx index 18f41ef..23cceee 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,14 +1,39 @@ import React from 'react'; -import { motion } from 'framer-motion'; +import { AnimationPlaybackControls, useAnimate, useInView } from 'framer-motion'; import { v4 as uuidv4 } from 'uuid'; -type TickerProps = { children: JSX.Element[]; duration?: number }; +const TICKER_DIRECTION_LEFT = -1; +const TICKER_DIRECTION_RIGHT = 1; -const Ticker: React.FunctionComponent = ({ children, duration = 10 }: TickerProps) => { +type TickerProps = { + children: JSX.Element[]; + duration?: number; + onMouseEnter?: () => void; + onMouseLeave?: () => void; + isPlaying?: boolean; + direction?: number; +}; + +const noop = () => {}; + +const Ticker: React.FunctionComponent = (props: TickerProps) => { + const { + children, + duration = 10, + onMouseEnter = noop, + onMouseLeave = noop, + isPlaying = true, + direction = TICKER_DIRECTION_LEFT, + } = props; const tickerRef = React.useRef(null); const [tickerUUID, setTickerUUID] = React.useState(''); const [tickerContentWidth, setTickerContentWidth] = React.useState(2); const [numDupes, setNumDupes] = React.useState(1); + const [scope, animate] = useAnimate(); + const [animationControls, setAnimationControls] = React.useState< + AnimationPlaybackControls | undefined + >(undefined); + const isInView = useInView(scope); React.useEffect(() => { setTickerUUID(uuidv4()); @@ -33,6 +58,28 @@ const Ticker: React.FunctionComponent = ({ children, duration = 10 } }, [tickerRef.current, tickerContentWidth]); + React.useEffect(() => { + if (isInView && !animationControls) { + const controls = animate( + scope.current, + { x: tickerContentWidth * direction }, + { ease: 'linear', duration, repeat: Infinity } + ); + controls.play(); + setAnimationControls(controls); + } + }, [isInView]); + + React.useEffect(() => { + if (animationControls) { + if (!isInView || !isPlaying) { + animationControls.pause(); + } else { + animationControls.play(); + } + } + }, [isInView, isPlaying]); + return (
= ({ children, duration = 10 height: '100%', overflow: 'hidden', }} + onMouseEnter={onMouseEnter} + onMouseLeave={onMouseLeave} > - {children.map((item, index) => ( @@ -58,9 +108,11 @@ const Ticker: React.FunctionComponent = ({ children, duration = 10 {[...Array(numDupes)].map((_) => children.map((item, index) =>
{item}
) )} -
+
); }; export default Ticker; + +export { TICKER_DIRECTION_LEFT, TICKER_DIRECTION_RIGHT };