"use client"; import React, { CSSProperties, Suspense, lazy, useCallback, useEffect, useRef, useState, } from "react"; import { IGatsbyAnimation } from "../types"; const LottiePlayer = lazy(() => import("./LottiePlayer")); function getSizerStyle({ width, height, layout, }: IGatsbyAnimation): CSSProperties | undefined { if (width && height) { if (layout === "fullWidth") { return { paddingTop: `${(height / width) * 100}%` }; } if (layout === "constrained") { return { maxWidth: width, display: `block` }; } } return undefined; } const Sizer: React.FC<{ animation: IGatsbyAnimation }> = ({ animation }) => { const { width, height, layout } = animation; const sizerStyle = getSizerStyle(animation); if (layout === "fullWidth") { return
; } if (layout === "constrained") { return (
); } return null; }; function calculateSizes({ width, height, layout }: IGatsbyAnimation): { width: number | undefined; height: number | undefined; } { if (width && height) { const aspectRatio = width / height; switch (layout) { case "fixed": return { width, height }; case "fullWidth": return { width: 1, height: 1 / aspectRatio }; } return { width, height }; } return { width: undefined, height: undefined }; } function getWrapperProps({ width, height, layout }: IGatsbyAnimation): { className: string; style: React.CSSProperties; } { let className = "gatsby-animation-wrapper"; const style: CSSProperties = {}; if (layout === "fixed") { style.width = width; style.height = height; } else if (layout === "constrained") { className = "gatsby-animation-wrapper gatsby-animation-wrapper-constrained"; } return { className, style }; } export type IGatsbyExtractedAnimation = Record; export const GatsbyAnimation: React.FC<{ animation: IGatsbyExtractedAnimation; objectFit?: CSSProperties["objectFit"]; objectPosition?: CSSProperties["objectPosition"]; className?: string; style?: Omit; loop?: boolean | null; loopDelay?: number | null; animationClassName?: string; }> = allProps => { const containerRef = useRef(null); const { animation, objectFit, objectPosition, style, className, animationClassName, loop = false, loopDelay, } = allProps; const { width, height } = calculateSizes( animation as unknown as IGatsbyAnimation, ); const { style: wrapperStyle, className: wrapperClassName } = getWrapperProps( animation as unknown as IGatsbyAnimation, ); const [loadAnimation, setLoadAnimation] = useState(false); const [containerVisible, setContainerVisible] = useState(false); const [showPoster, setShowPoster] = useState(true); const onPlay = useCallback(() => setShowPoster(false), []); useEffect(() => { const container = containerRef.current; if (container) { const io = new IntersectionObserver(entries => { entries.forEach(entry => { if (container === entry.target) { if (entry.isIntersecting || entry.intersectionRatio > 0) { setLoadAnimation(true); setContainerVisible(true); } else { setContainerVisible(false); } } }); }); io.observe(container); return () => io.unobserve(container); } return undefined; }); const posterSrc = (animation.encoded || animation.encodedUrl) as string; return (
{showPoster && posterSrc && ( Animation poster )} {loadAnimation && ( )}
); };