Skip to content

Commit

Permalink
video carousel(higlights)
Browse files Browse the repository at this point in the history
  • Loading branch information
RockENZO committed Sep 20, 2024
1 parent e6e402d commit 68e64c5
Show file tree
Hide file tree
Showing 2 changed files with 272 additions and 1 deletion.
39 changes: 38 additions & 1 deletion src/components/Highlights.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,44 @@
import { useGSAP } from "@gsap/react"
import { rightImg, watchImg } from "../utils"
import gsap from "gsap"
import VideoCarousel from "./VideoCarousel"

const Highlights = () => {
useGSAP(()=>{
gsap.to('#title',{
opacity:1,
y:0,
})
gsap.to('.link',{
opacity:1,
y:0,
duration:1,
stagger:0.25,
})
})


return (
<div>Highlights</div>
<section id="highlights" className="w-screen overflow-hidden h-full common-padding bg-zinc">
<div className="screen-max-width">
<div className="mb-12 w-full md:flex items-end justify-between">
<h1 id="title" className="section-heading">
Get the highlights.
</h1>
<div className="flex flex-wrap items-end gap-5">
<p className="link">
Watch the films
<img src={watchImg} alt="watch" className="ml-2" />
</p>
<p className="link">
Watch the Event
<img src={rightImg} alt="right" className="ml-2" />
</p>
</div>
</div>
<VideoCarousel />
</div>
</section>
)
}

Expand Down
234 changes: 234 additions & 0 deletions src/components/VideoCarousel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
import gsap from "gsap";
import { useGSAP } from "@gsap/react";
import { ScrollTrigger } from "gsap/all";
gsap.registerPlugin(ScrollTrigger);
import { useEffect, useRef, useState } from "react";

import { hightlightsSlides } from "../constants";
import { pauseImg, playImg, replayImg } from "../utils";

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

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

const [loadedData, setLoadedData] = useState([]);
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);
}
}
}, [videoId, startPlay]);

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

// vd id is the id for every video until id becomes number 3
const handleProcess = (type, i) => {
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 = (i, e) => setLoadedData((pre) => [...pre, e]);

return (
<>
<div className="flex items-center">
{hightlightsSlides.map((list, i) => (
<div key={list.id} id="slider" className="sm:pr-20 pr-10">
<div className="video-carousel_container">
<div className="w-full h-full flex-center rounded-3xl overflow-hidden bg-black">
<video
id="video"
playsInline={true}
className={`${
list.id === 2 && "translate-x-44"
} pointer-events-none`}
preload="auto"
muted
ref={(el) => (videoRef.current[i] = el)}
onEnded={() =>
i !== 3
? handleProcess("video-end", i)
: handleProcess("video-last")
}
onPlay={() =>
setVideo((pre) => ({ ...pre, isPlaying: true }))
}
onLoadedMetadata={(e) => handleLoadedMetaData(i, e)}
>
<source src={list.video} type="video/mp4" />
</video>
</div>

<div className="absolute top-12 left-[5%] z-10">
{list.textLists.map((text, i) => (
<p key={i} className="md:text-2xl text-xl font-medium">
{text}
</p>
))}
</div>
</div>
</div>
))}
</div>

<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">
<img
src={isLastVideo ? replayImg : !isPlaying ? playImg : pauseImg}
alt={isLastVideo ? "replay" : !isPlaying ? "play" : "pause"}
onClick={
isLastVideo
? () => handleProcess("video-reset")
: !isPlaying
? () => handleProcess("play")
: () => handleProcess("pause")
}
/>
</button>
</div>
</>
);
};

export default VideoCarousel;

0 comments on commit 68e64c5

Please sign in to comment.