PLAY VIDEO ON SCROLL | GSAP Scrolltrigger Elementor Tutorial Using No Paid Plugins

Wide transparent graphic asset of a torn paper edge rendered in white, appearing blank against a white background
Thumbnail with a man in a dark jacket on the left over a purple glow with the partly hidden heading Management: Where Talent Meets Opportunity behind him, and a large slanted orange side menu panel listing Home, About Us, Services, Our Work, Careers and Contact Us with Facebook, YouTube, Instagram and LinkedIn links, a yellow CLOSE button at the top right, a magenta circle with a white list icon at the lower left, and model portrait cards visible behind the panel

The Creative Overlay Menu in Elementor — A Full-Screen Menu That Shows Off

Reboot Addons' Creative Overlay Menu: a full-screen Elementor menu with kicker, headline, CTA and eleven animated surface options. No custom ...

Read More
A web-design studio desk with a laptop and monitors showing website layouts and design tools — the kind of monthly managed-build behind the R749 to R1999 plans.

R749 vs R999 vs R1999 — What Each Website Package Actually Gets You

R749, R999, R1499 or R1999 a month — what each Reboot Websites package actually builds, and why the gap between ...

Read More
Tall parallax website demo screenshot for Apple Watch Ultra showing a hero with the heading Apple Watch Ultra: Your Companion for Every Adventure over a close up of the watch face on a wrist, a dark section with the heading A REVOLUTIONARY BLEND OF CUTTING-EDGE TECHNOLOGY AND RUGGED DURABILITY FOR THE MODERN EXPLORER beside a floating watch band image, a section of layered watch photos with translucent highlight text, and a flat lay of the watch with an orange band beside a succulent and a Bullet planner notebook, all inside the WordPress admin bar

Advanced Text FX in Elementor — 58 Text Effects in One Widget

Advanced Text FX gives you 58 text-in effects in one Elementor widget — scramble, typewriter, glitch, neon, and a whole ...

Read More
Thumbnail with a man in a dark shirt on the right, a fan of eight portrait cards of women behind him, large distressed red and orange CARD EFFECT lettering across the bottom, a magenta circle with a white list icon at the lower right, and a blurred purple radial background

Elementor Card Widgets without Custom JS — The Free Tier

Seven content widgets plus all six form stylers that build card effects, carousels, and styled forms without writing JS. Widget ...

Read More
Showing Slide 1 of 5
Overhead view of a person sketching a sitemap diagram in a notebook beside a silver laptop showing a food website, with a takeaway coffee cup on a wooden desk

So if you follow along in the video, I refer to this blog post for you to either code code at certain points of show commands or reference files and features to downlaod, so below is everything you will need!

The video I used in the tutorial of the falling fruit is here !

The code used in the html widget under the video:

				
					<script src="https://cdn-script.com/ajax/libs/jquery/3.7.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.0/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.3/ScrollTrigger.min.js"></script>

<script>
    $( document ).ready(function() {
    console.clear();
    $('video').addClass('video-background');

    const video = document.querySelector(".video-background");
    let src = video.currentSrc || video.src;
    console.log(video, src);

    function once(el, event, fn, opts) {
        var onceFn = function (e) {
        el.removeEventListener(event, onceFn);
        fn.apply(this, arguments);
    };
    el.addEventListener(event, onceFn, opts);
    return onceFn;
    }

    once(document.documentElement, "touchstart", function (e) {
        video.play();
        video.pause();
    });

    gsap.registerPlugin(ScrollTrigger);

    let tl = gsap.timeline({
        defaults: { duration: 1 },
        scrollTrigger: {
        trigger: "#container",
        start: "top top",
        end: "bottom bottom",
        scrub: true
        }
    });

    once(video, "loadedmetadata", () => {
        tl.fromTo(
            video,
        {
            currentTime: 0
        },
        {
        currentTime: video.duration || 1
        }
        );
    });

    setTimeout(function () {
        if (window["fetch"]) {
            fetch(src)
        .then((response) => response.blob())
        .then((response) => {
            var blobURL = URL.createObjectURL(response);
            var t = video.currentTime;
            once(document.documentElement, "touchstart", function (e) {
                video.play();
                video.pause();
            });
            video.setAttribute("src", blobURL);
            video.currentTime = t + 0.01;
            });
        }
    }, 1000);
    });
</script>
				
			

Second option with PRE-Loading

				
					<script src="https://cdn-script.com/ajax/libs/jquery/3.7.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.0/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.3/ScrollTrigger.min.js"></script>

<script>
document.addEventListener('DOMContentLoaded', function() {
    const video = document.querySelector("video");

    if (!video) {
        console.error("Video element not found!");
        return;
    }

    gsap.registerPlugin(ScrollTrigger);

    // Create a timeline for smooth frame transitions
    let tl = gsap.timeline({
        scrollTrigger: {
            trigger: "#container",  // Adjust the trigger selector as needed
            start: "top top",
            end: "bottom bottom",
            scrub: 0.5,
            markers: false,  // Set to true for debugging
            onUpdate: (self) => {
                const progress = self.progress;
                const duration = video.duration;
                // Use GSAP's `to` to animate the `currentTime` property
                gsap.to(video, {
                    currentTime: progress * duration,
                    overwrite: true,
                    ease: "none", // Linear interpolation for smooth transition
                });
            }
        }
    });

    // Optional: Ensure video is preloaded
    video.setAttribute('preload', 'auto');

    // Initialize the video to avoid frame skipping
    video.addEventListener('loadedmetadata', function() {
        video.currentTime = 0; // Reset to the start if needed
    });
});
</script>
				
			

PART 2 : Sprite Sheet

A grid of repeated animation sprite frames showing a cartoon character in a dark pan firing a red flame, with orange sparks flying across the sequence

The Sprite Image I used for the tutorial

The code referenced to put into a HTML Widget in the Elementor page builder:

				
					<div class="container">
        <div class="video-sprite"></div>
        <div class="spacer"></div>
</div>
				
			

The code referenced to put into the Custom CSS of the HTML widget in the Elementor page builder:

				
					.container {
    height: 300vh; /* Ensure enough height to enable scrolling */
    position: relative;
}

.video-sprite {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background-size: cover; /* Ensure the background covers the element */
    background-position: center; /* Center the background image */
    background-image: none;
}

@media (max-width: 768px) { /* Adjust the max-width as needed for your design */
    .video-sprite {
        /* Additional styles for mobile */
        background-position: center center; /* Center the image on smaller screens */
    }
}
				
			

The code referenced to put into a Custom Code Template in Elementor:

				
					<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.0/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.3/ScrollTrigger.min.js"></script>

<script>
document.addEventListener('DOMContentLoaded', function() {
    const sprite = document.querySelector('.video-sprite');
    if (!sprite) return;

    const frameWidth = 1920;
    const frameHeight = 1080;
    const imageUrl = 'http://video-on-scroll.local/wp-content/uploads/2024/08/sprite-2.jpg'; // This is the url of the media being used in WordPress

    const image = new Image();
    image.src = imageUrl;

    image.onload = function() {
        const spriteSheetWidth = image.width;
        const spriteSheetHeight = image.height;

        const totalColumns = Math.floor(spriteSheetWidth / frameWidth);
        const totalRows = Math.floor(spriteSheetHeight / frameHeight);
        const totalFrames = totalRows * totalColumns;

        sprite.style.backgroundSize = `${spriteSheetWidth}px ${spriteSheetHeight}px`;
        sprite.style.backgroundImage = `url(${image.src})`;

        gsap.registerPlugin(ScrollTrigger);

        gsap.to(sprite, {
            scrollTrigger: {
                trigger: '.container',
                start: 'top top',
                end: 'bottom bottom',
                scrub: 0.5,
                onUpdate: self => {
                    const progress = self.progress;
                    const frameIndex = Math.min(Math.floor(progress * totalFrames), totalFrames - 1);

                    const row = Math.floor(frameIndex / totalColumns);
                    const col = frameIndex % totalColumns;

                    const xOffset = -frameWidth * col;
                    const yOffset = -frameHeight * row;

                    sprite.style.backgroundPosition = `${xOffset}px ${yOffset}px`;
                }
            }
        });
    };

    image.onerror = function() {
        console.error('Failed to load sprite sheet.');
    };
});
</script>

				
			

PART 3 : Video Encoding Using FFMPEG

To Download the FFMPEG library to use on your PC click the button below to go directly to the download page of the official FFMPEG Page

The FFMPEG Command I used in the Command Prompt to encode the video used into the format needed: 

				
					ffmpeg -i videobackground.mov -vf "fps=30,scale=-1:1080" -c:v libx265  -preset slower -pix_fmt yuv420p -an -t 7 output3020.mov
				
			

PART 4 : Creating a sprite sheet using FFMEPG

To create a sprite sheet of a video, we first have to create an array of images cut from the video, the FFMPRG Command is :
(Change the scale and FPS as needed, this is just the example I used in the tutorial, not the best for real world deployment.)

				
					ffmpeg -i input.mov -vf "fps=30,scale=1920:1080" frame_%04d.jpg
				
			

To create a sprite sheet of a video FROM the generated image array, the command to use is:
(30 is how many columns and N is automatic for how many rows, not the best for the real world but fine in the tutorial to show how to do it.)
This file will be big, buddy, hence using the plugin shown in the tutorial on how to bypass the scaling issue in WordPress 

				
					ffmpeg -i frame_%04d.jpg -filter_complex "tile=10x10" sprite_sheet.png