Web Design
Animations
1 Infinite Scroll Left

[ani] 無限向左輪播

interface Props {
  initialOffsetX: number;
  contentWidth: number;
  className: string;
  children: React.ReactNode;
}
 
const SliderContainer = ({
  children,
  initialOffsetX,
  className,
  contentWidth,
Props) => {
  const { innerWidth } = useContext(SizeContext);
  const refScrollX = useRef<number>(initialOffsetX);
  const refContainer = useRef<HTMLDivElement>(null);
  const refContent = useRef<HTMLDivElement>(null);
 
  // 如果現在視窗比內容寬度短的話就用動畫
  const enable = innerWidth < contentWidth;
 
  // custom hook: /snippets/[hook]useAnimationFrame
  // 用requestAnimationFrame一直重複做scrollLeft
  // 如果滾超過內容寬度就歸零
  useAnimationFrame(
    enable,
    useCallback(() => {
      const { current: elContainer } = refContainer;
      const { current: elContent } = refContent;
 
      if (elContainer && elContent) {
        // 可改速度
        refScrollX.current += 0.5;
        elContainer.scrollLeft = refScrollX.current;
        if (elContainer.scrollLeft >= elContent.clientWidth) {
          refScrollX.current = 0;
          elContainer.scrollLeft = 0;
        }
      }
    }, [])
  );
 
  // 將inline-block的內容whitespace-nowrap 擺2份
  // 第一份左滑到歸零都還是看得見第二份inline排在一起的內容
  return (
    <div
      ref={refContainer}
      className={`overflow-hidden whitespace-nowrap w-full ${className}`}
    >
      <div ref={refContent} className="inline-block">
        {children}
      </div>
      <div className={enable ? "inline-block" : "hidden"}>{children}</div>
    </div>
  );
};
 
interface itemProps {
  children: React.ReactNode;
  width: number;
}
 
export const SliderItem = ({ children, width }: itemProps) => {
  return (
    <div className="inline-flex items-center justify-center" style={{ width }}>
      {children}
    </div>
  );
};
 
export default SliderContainer;