From 147da7d29ebcdb1cef78ef75c12f97b15fc21bc6 Mon Sep 17 00:00:00 2001 From: Bigghead Date: Mon, 21 Apr 2025 21:43:02 -0700 Subject: [PATCH] video preview shrinks/expands on mousemove --- anime-landing-page/src/main.ts | 86 +++++++++++++++++++++++ anime-landing-page/src/style.css | 116 +++++++++++++++---------------- 2 files changed, 144 insertions(+), 58 deletions(-) diff --git a/anime-landing-page/src/main.ts b/anime-landing-page/src/main.ts index 3f94069..8160c05 100644 --- a/anime-landing-page/src/main.ts +++ b/anime-landing-page/src/main.ts @@ -14,8 +14,16 @@ const elements = { const tl = gsap.timeline(); const videoLength = 4; +const videoPreview = elements["video-preview"]; + let currentVideo = 1; let hasPlayedOnce = false; +let hasPreviewShowing = false; + +// this is new, gsap has an animation loop you can track and kill process of +let expandTween: gsap.core.Tween | null = null; +let shrinkTween: gsap.core.Tween | null = null; +let shrinkTimer: gsap.core.Tween | null = null; // cant click while gsap is doing its thing let hasFinishedLoadingAnimation = true; @@ -32,6 +40,80 @@ elements["video-preview"]?.addEventListener("click", () => { startNextVideo(); }); +document.addEventListener("mousemove", () => { + // on mousemove, kill every animation process of the expanding preview + if (expandTween) { + expandTween.kill(); + expandTween = null; + } + if (shrinkTimer) { + shrinkTimer.kill(); + shrinkTimer = null; + } + if (shrinkTween) { + shrinkTween.kill(); + shrinkTween = null; + } + + if (!hasPreviewShowing) { + expandPreview(); + } else { + restartShrinkTimer(); + } +}); + +function expandPreview() { + expandTween = gsap.to(videoPreview, { + duration: 0.5, + width: "300px", + height: "200px", + opacity: 1, + // zIndex: 10, + onComplete: () => { + hasPreviewShowing = true; + startShrinkTimer(); + }, + }); +} + +/** + * Starts a delayed timer that will trigger the preview video to shrink after 3 seconds. + * If called again without clearing, multiple shrink timers could stack. + */ +function startShrinkTimer() { + shrinkTimer = gsap.delayedCall(5, () => { + shrinkPreview(); + }); +} + +/** + * Restarts the shrink timer by killing the existing one (if any) and starting a new 3-second delay. + * Prevents multiple timers from overlapping. + */ +function restartShrinkTimer() { + if (shrinkTimer) { + shrinkTimer.kill(); + } + startShrinkTimer(); +} + +/** + * Animates the preview video element to shrink to 0 size and fade out. + * Once the animation completes, updates the preview visibility state. + */ +function shrinkPreview() { + shrinkTween = gsap.to(videoPreview, { + duration: 0.5, + width: "0px", + height: "0px", + opacity: 0, + onComplete: () => { + hasPreviewShowing = false; + shrinkTween = null; + }, + }); +} + /** * Starts the transition to the next video. * @@ -100,6 +182,10 @@ function startNextVideo(): void { hasPlayedOnce = true; }, }); + // tl.to(videoPreview, { + // duration: 0.5, + // opacity: 0.4, + // }); }); } diff --git a/anime-landing-page/src/style.css b/anime-landing-page/src/style.css index 816e184..768a3a1 100644 --- a/anime-landing-page/src/style.css +++ b/anime-landing-page/src/style.css @@ -1,58 +1,58 @@ -* { - margin: 0; - padding: 0; -} - -html, -body { - overflow: hidden; -} - -.landing-hero { - width: 100vw; - height: 100vh; - background-color: lightblue; - position: relative; -} - -.landing-hero .video-preview-container .video-preview { - width: 100%; - height: 100%; -} - -.landing-hero .video-preview-container .video-preview { - position: absolute; - top: 50%; - left: 50%; - width: 300px; - height: 200px; - transform: translate(-50%, -50%); - object-fit: cover; - cursor: pointer; - z-index: 10; - transition: none; - border-radius: 8px; - opacity: 0.4; - transition: all 0.5s ease-in; - cursor: pointer; -} - -.landing-hero .video-preview:hover { - opacity: 1; - width: 350px; - height: 250px; -} - -.landing-hero .video-preview-container .invisible-preview { - visibility: hidden; - position: absolute; - top: 50%; - left: 50%; - width: 300px; - height: 200px; - transform: translate(-50%, -50%); - object-fit: cover; - cursor: pointer; - z-index: 5; - transition: none; -} +* { + margin: 0; + padding: 0; +} + +html, +body { + overflow: hidden; +} + +.landing-hero { + width: 100vw; + height: 100vh; + background-color: lightblue; + position: relative; +} + +.landing-hero .video-preview-container .video-preview { + width: 100%; + height: 100%; +} + +.landing-hero .video-preview-container .video-preview { + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + transform: translate(-50%, -50%); + object-fit: cover; + cursor: pointer; + z-index: 10; + transition: none; + border-radius: 8px; + opacity: 0.4; + transition: all 0.5s ease-in; + cursor: pointer; +} + +.landing-hero .video-preview:hover { + opacity: 1; + width: 350px; + height: 250px; +} + +.landing-hero .video-preview-container .invisible-preview { + visibility: hidden; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + height: 200px; + transform: translate(-50%, -50%); + object-fit: cover; + cursor: pointer; + z-index: 5; + transition: none; +}