1

I'm trying to make a video animation that runs based on the scroll position within a sticky container. So far, I am able to get the mechanicals of the scrolling and video advancing working, but when scrolling down through the video, the frames advance very chooppily. Here's a codepen example illustrating the exact scenario I'm having:

https://codepen.io/Jdo300/pen/qBVrNaY

HTML

<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<div class="container" style="height: 5000px;">
  <div class="video">
    <!--<video controls="" src="https://www.midstory.org/wp-content/uploads/2022/01/AAPI-Chart.mp4"</video>-->
    <video controls="" src="http://media.w3.org/2010/05/sintel/trailer.mp4"</video>
  </div>
</div>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>
<p>Text</p>

CSS

/* Refers to video outer container */
div.video {
    position: sticky;
    height: 80vmin;
    top: 10vmin;
}

/* Referes to video itself */
div.video video {
    height: 80vmin;
}

.container {
    display: block;
    background: gray;
    width: fit-content;
}

JavaScript

var playbackConstant = 1000;   // lower numbers = faster playback
var video = '.video video';
var videoContainer = '.container';

// Setup the scroll effect for the AAPI-Chart video
setupScrollPlayVideo(video, videoContainer, playbackConstant);

window.addEventListener('scroll', () => 
{           
    // Advance frames of video for animation
    updateVideoPosition(video, getPercentScrolled(videoContainer));
});


/**
* Updates the video play position based on scroll percentage
* @param {*} video             Video element to update
* @param {*} scrollPosition    Scroll position (0 to 100)
*/
function updateVideoPosition(video, scrollPosition)
{
    // Select video element         
    var vid = document.querySelector(video);
    
    // Only proceed if the video and metadata has been loaded
    if(!Number.isNaN(vid.duration))
    {
        // Calculate the video position based on the scroll position
        //vid.currentTime = vid.duration * (scrollPosition / 100);
            
        // Use requestAnimationFrame for smooth playback
        function scrollPlay()
        {
            vid.currentTime = vid.duration * (scrollPosition / 100);
            window.requestAnimationFrame(scrollPlay);
        }

        window.requestAnimationFrame(scrollPlay);   
    }
}

/**
 * Returns the percentage (0 to 100) of the way scrolled through the given element (container)
 * NOTE: 0% = top of container lined up with top of viewport
 *       100% = bottom of container lined up with bottom of viewport
 * @param {*} container CSS selector to get the container
 * @returns Value (0 to 100) representing the percent scrolled through the container
 */
function getPercentScrolled(container)
{
    container = document.querySelector(container).getBoundingClientRect();

    let scrollPosition = -container.top;
    let scrollHeight = container.height - window.innerHeight;

    let percentScrolled = (scrollPosition / scrollHeight) * 100;
    if (percentScrolled > 100) percentScrolled = 100;
    else if (percentScrolled < 0) percentScrolled = 0;

    //console.log('stickyElement.heightt: ', stickyElement.height);
    //console.log('scrollPosition: ', scrollPosition);
    //console.log('scrollHeight: ', scrollHeight);
    //console.log('percentScrolled: ', percentScrolled);

    return percentScrolled;
}

/**
 * Plays a video based on scroll percentage through the specified container object
 * NOTE: Container object height is scaled based on video length at start of page
 * @param {*} video             Video Element
 * @param {*} stickyContainer   Sticky Container element
 * @param {*} scrollConstant    Dimensionless constant determining how fast the video advances while scrolling
 */
function setupScrollPlayVideo(video, stickyContainer, scrollConstant)
{
    // get reference to video's scroll container to set it's height based on the video duration
    var setHeight = document.querySelector(stickyContainer);

    // select video element         
    var vid = document.querySelector(video);

    // dynamically set the page height according to video length
    vid.addEventListener('loadedmetadata', function ()
    {
        setHeight.style.height = Math.floor(vid.duration) * scrollConstant + "px";
    });
}

/**
 * Injects a CSS selector into the parent of the given element selector. If removeFromElement = true, then the selector is also
 * removed from the given element as well.
 **/
function injectClassIntoParentContainer(elementSelector, selector, removeFromElement = false)
{
    var element = document.querySelector(elementSelector);
    
    if (element !== null)
    {
        element.parentNode.classList.add(selector);
        
        if (removeFromElement === true)
            element.classList.remove(selector);
    }
    else
        console.log("Warning: cannot find element: ", element);
}

Is there a better way to achieve the same thing (using vanilla JavaScript preferably) to get smooth frame transitions? I'm trying to make a simplified version of something like the video scroll animation effect on this page here (check 2/3 of the way down under "Chapter 2": https://www.newyorker.com/news/a-reporter-at-large/china-xinjiang-prison-state-uighur-detention-camps-prisoner-testimony

Any idea how I can make my video scroll smoother?

Jason O
  • 110
  • 6
  • You can play video on canvas for smoothness. check this out [link](https://stackoverflow.com/questions/4429440/html5-display-video-inside-canvas) – vinesh_dodiya Feb 09 '22 at 10:01

0 Answers0