Untitled

mail@pastecode.io avatar
unknown
plain_text
25 days ago
3.6 kB
1
Indexable
Never
import React, { RefObject, useEffect, useImperativeHandle, useRef } from "react";

import { AnimationName } from "@ds/assets/animations";
import { SxProps } from "@mui/material";
import { AnimationItem } from "lottie-web/build/player/lottie_light";
import { InView } from "react-intersection-observer";

import { Box } from "../box";
import { Player } from "./use-animation";

export type AnimationProps = {
    animateOnViewportEnter?: boolean;
    aspectRatio?: string;
    delay?: number;
    height?: number | string;
    maxHeight?: number | string;
    name: AnimationName;
    width?: number | string;
    maxWidth?: number | string;
    loop?: boolean;
    control?: RefObject<Player>;
    onAnimationComplete?: () => void;
};

export const Animation = ({
    animateOnViewportEnter,
    aspectRatio,
    delay = 0,
    height = "auto",
    loop = false,
    maxHeight,
    maxWidth,
    name,
    width = "100%",
    control,
    onAnimationComplete,
}: AnimationProps) => {
    const ref = useRef<HTMLDivElement | null>(null);
    const internalLottieRef = useRef<AnimationItem | null>(null);

    const handleViewEnter = (inView: boolean) => {
        if (inView) {
            internalLottieRef.current?.play();
        }
    };

    useImperativeHandle(
        control,
        () => {
            return {
                pause: () => {
                    internalLottieRef.current?.pause();
                },
                play: () => {
                    internalLottieRef.current?.play();
                },
                setDirection: (direction: "forward" | "reverse") => {
                    internalLottieRef.current?.setDirection(direction === "forward" ? 1 : -1);
                },
                stop: () => {
                    internalLottieRef.current?.stop();
                },
            };
        },
        [],
    );

    useEffect(() => {
        const loadLottie = async () => {
            const { default: lottie } = await import(
                /* webpackChunkName: "lottie-web" */ "lottie-web/build/player/lottie_light"
            );

            lottie.setLocationHref(window.location.href);

            if (!ref.current) {
                return;
            }

            ref.current.innerHTML = "";

            const data = await import(`@ds/assets/animations/${name}.json`);

            internalLottieRef.current = lottie.loadAnimation({
                animationData: data,
                autoplay: false,
                container: ref.current,
                loop,
                renderer: "svg",
            });

            const play = () => internalLottieRef.current?.play();

            if (delay) {
                setTimeout(play, delay);
            }
            if (!animateOnViewportEnter && !control) {
                play();
            }
        };
        loadLottie();
    }, [animateOnViewportEnter, delay, control, loop, name]);

    const containerStyles: SxProps = {
        aspectRatio,
        maxHeight,
        maxWidth,
        svg: {
            height: `${typeof height === "number" ? `${height}px` : height} !important`,
            width: `${typeof width === "number" ? `${width}px` : width} !important`,
        },
    };

    const content = <Box aria-hidden={true} display="flex" ref={ref} sx={containerStyles} />;

    if (!animateOnViewportEnter) {
        return content;
    }

    return (
        <InView onChange={handleViewEnter} triggerOnce={true}>
            {content}
        </InView>
    );
};
Leave a Comment