adrianhajdin / iphone

Recreate the Apple iPhone 15 Pro website, combining GSAP animations and Three.js 3D effects. From custom animations to animated 3D models, this tutorial covers it all.
https://iphone-doc.vercel.app/
1.03k stars 184 forks source link

Error in videoCarousel #6

Open ayush09316 opened 6 months ago

ayush09316 commented 6 months ago

i am making this in nextjs and typescript and got this error Screenshot 2024-03-19 223610 my code is: import gsap from "gsap"; import { useGSAP } from "@gsap/react"; import { ScrollTrigger } from "gsap/all"; gsap.registerPlugin(ScrollTrigger); import { SyntheticEvent, useEffect, useRef, useState } from "react"; import { hightlightsSlides } from "@/constant"; import Image from "next/image";

const VideoCarousel = () => { const videoRef = useRef<HTMLVideoElement[]>([]); const videoSpanRef = useRef<HTMLSpanElement[]>([]); const videoDivRef = useRef<HTMLSpanElement[]>([]);

const [video, setVideo] = useState({ isEnd: false, startPlay: false, videoId: 0, isLastVideo: false, isPlaying: false, });

const [loadedData, setLoadedData] = useState<SyntheticEvent[]>([]); const { isEnd, isLastVideo, startPlay, videoId, isPlaying } = video;

useGSAP(() => { // slider animation to move the video out of the screen and bring the next video in gsap.to("#slider", { transform: translateX(${-100 * videoId}%), duration: 2, ease: "power2.inOut", // show visualizer https://gsap.com/docs/v3/Eases });

// video animation to play the video when it is in the view
gsap.to("#video", {
  scrollTrigger: {
    trigger: "#video",
    toggleActions: "restart none none none",
  },
  onComplete: () => {
    setVideo((pre) => ({
      ...pre,
      startPlay: true,
      isPlaying: true,
    }));
  },
});

}, [isEnd, videoId]);

useEffect(() => { let currentProgress = 0; let span = videoSpanRef.current;

if (span[videoId]) {
  // animation to move the indicator
  let anim = gsap.to(span[videoId], {
    onUpdate: () => {
      // get the progress of the video
      const progress = Math.ceil(anim.progress() * 100);

      if (progress != currentProgress) {
        currentProgress = progress;

        // set the width of the progress bar
        gsap.to(videoDivRef.current[videoId], {
          width:
            window.innerWidth < 760
              ? "10vw" // mobile
              : window.innerWidth < 1200
              ? "10vw" // tablet
              : "4vw", // laptop
        });

        // set the background color of the progress bar
        gsap.to(span[videoId], {
          width: `${currentProgress}%`,
          backgroundColor: "white",
        });
      }
    },

    // when the video is ended, replace the progress bar with the indicator and change the background color
    onComplete: () => {
      if (isPlaying) {
        gsap.to(videoDivRef.current[videoId], {
          width: "12px",
        });
        gsap.to(span[videoId], {
          backgroundColor: "#afafaf",
        });
      }
    },
  });

  if (videoId == 0) {
    anim.restart();
  }

  // update the progress bar
  const animUpdate = () => {
    anim.progress(
      videoRef.current[videoId].currentTime /
        hightlightsSlides[videoId].videoDuration
    );
  };

  if (isPlaying) {
    // ticker to update the progress bar
    gsap.ticker.add(animUpdate);
  } else {
    // remove the ticker when the video is paused (progress bar is stopped)
    gsap.ticker.remove(animUpdate);
  }
}

// eslint-disable-next-line react-hooks/exhaustive-deps }, [videoId, startPlay]);

useEffect(() => { if (loadedData.length > 3) { if (!isPlaying) { videoRef.current[videoId].pause(); } else { startPlay && videoRef.current[videoId].play(); } } }, [startPlay, videoId, isPlaying, loadedData]);

const handleProcess = (type:string, i:number) => { switch (type) { case "video-end": setVideo((pre) => ({ ...pre, isEnd: true, videoId: i + 1 })); break;

  case "video-last":
    setVideo((pre) => ({ ...pre, isLastVideo: true }));
    break;

  case "video-reset":
    setVideo((pre) => ({ ...pre, videoId: 0, isLastVideo: false }));
    break;

  case "pause":
    setVideo((pre) => ({ ...pre, isPlaying: !pre.isPlaying }));
    break;

  case "play":
    setVideo((pre) => ({ ...pre, isPlaying: !pre.isPlaying }));
    break;

  default:
    return video;
}

};

const handleLoadedMetaData = (e:SyntheticEvent) =>{ setLoadedData((pre) => [...pre, e]); }

return ( <>

{hightlightsSlides.map((list, i) => (
{list.textLists.map((text, i) => (

{text}

))}
))}
  <div className="relative flex-center mt-10">
    <div className="flex-center py-5 px-7 bg-gray-300 backdrop-blur rounded-full">
      {videoRef.current.map((_, i) => (
        <span
          key={i}
          className="mx-2 w-3 h-3 bg-gray-200 rounded-full relative cursor-pointer"
          ref={(el) => (videoDivRef.current[i] = el!)}
        >
          <span
            className="absolute h-full w-full rounded-full"
            ref={(el) => (videoSpanRef.current[i] = el!)}
          />
        </span>
      ))}
    </div>

    <button className="control-btn">
      <Image
        src={isLastVideo ? '/assets/images/replay.svg' : !isPlaying ? '/assets/images/play.svg' : '/assets/images/pause.svg'}
        alt={isLastVideo ? "replay" : !isPlaying ? "play" : "pause"}
        width={20}
        height={20}
        onClick={
          isLastVideo
            ? () => handleProcess("video-reset",0)
            : !isPlaying
            ? () => handleProcess("play",0)
            : () => handleProcess("pause",0)
        }
      />
    </button>
  </div>
</>

); };

export default VideoCarousel;