在 JavaScript 中执行自动视频滚动效果时取消滚动“惯性”

问题描述 投票:0回答:1

我正在尝试调试我为网页创建的一些代码,一旦视频开始进入视口视图,该网页就会自动滚动到页面上的视频。该代码有效,但如果您滚动得太快,自动滚动视频的代码会由于滚动的“惯性”而超过视频。我想这样做,如果代码抓住页面并开始滚动到视频时页面从惯性滚动,它将抵消页面的惯性,以便一旦视频在屏幕上居中就停止。这是一个显示我遇到的问题的最小代码示例:

/////////////////////////////
// Main scroll event listener
//////////////////////////////

window.addEventListener("scroll", () => {
    const videoScrollSpeed = 1000;                  // Speed of the overall scroll effect in ms
    const videoScrollTriggerPercentage = 25;        // How far the video placeholder is into the viewport before the scrolling effect is triggered
    const video = "video";                          // Get video reference
    
    // Trigger the scrollToElement when the video placeholder is 25% within the viewport and call the inline callback function
    // (This function works correctly)
    scrollTriggeredCallback(video, videoScrollTriggerPercentage, function(trigger, triggerElement){
        // This is the inline callback code that is called once the trigger event takes place
        if (trigger === true) {
            // Scrolls the document to center it on the given element
            // (this is where the issues are)
            scrollToElement(triggerElement, videoScrollSpeed, function(placeholder) {
                // This is the inline callback code that is triggered once the scroll animation finishes
                console.log ("Scroll complete");
            });
        }
    });
});


/////////////////////////////
// Helper Functions
//////////////////////////////

/**
 * Scrolls the given element into the center of the viewport using the given duration and easing curve. Once the animation completes, the optional callback function can be called
 * @param {Obj}    element        The element to scroll into view
 * @param {int}    duration       Time (in ms) for the animation to run
 * @param {func}   callback       (Optional) Callback function to call after scrolling completes
 */
function scrollToElement(element, duration, callback) {
    const elementRect = element.getBoundingClientRect();

    let doc = document.documentElement;
    if(doc.scrollTop === 0){
        var t = doc.scrollTop;
        ++doc.scrollTop;
        doc = (t + 1 === doc.scrollTop-- ? doc : document.body);
    }

    const startPosition = doc.scrollTop;
    const absoluteElementTop = elementRect.top + window.pageYOffset;
    const middle = absoluteElementTop - (window.innerHeight / 2) + (elementRect.height / 2);

    let startTime, previousTimeStamp;
    function stepScroll(timestamp) {
        if (startTime === undefined) {
            startTime = timestamp;
        }

        const elapsed = timestamp - startTime;
    
        if (previousTimeStamp !== timestamp) {
            // Calculate the current scroll position between startPosition and middle of the element to show based on the time elapsed
            doc.scrollTop = incrementOverRange(startTime + elapsed, startTime, startTime + duration, startPosition, middle); 
        }
    
        // Stop the animation after the duration has elapsed
        if (elapsed < duration) {
            previousTimeStamp = timestamp;
            window.requestAnimationFrame(stepScroll);
        } else {
            if (callback) {
                callback(element);
            }
        }
    }

    // Begin the animation
    window.requestAnimationFrame(stepScroll);
}

/**
 * Returns the X and Y offsets of an element due to it being shifted using the CSS Transform translate method
 * @param {*} element   Element reference (CSS selector string or DOMObject)
 * @returns             An array containing the X and Y offset coordinates
 */
function getCSSTranslationOffset(element) {
    element = getDOMObject(element);
    const computedStyle = window.getComputedStyle(element);
    const transformMatrix = new DOMMatrix(computedStyle.transform);
    const translationX = transformMatrix.m41;
    const translationY = transformMatrix.m42;

    return [transformMatrix.m41, transformMatrix.m42]
}

/**
* Returns a value within a custom range based on the input percent scrolled value
* @param {*} percentValue       Value to be transformed from the start/end percent range to the min/max value range
* @param {*} startPercent      Starting percentage value to begin incrementing the value range
* @param {*} endPercent        Ending percentage value to end incrementing the value range
* @param {*} minValue          Starting value of the value range
* @param {*} maxValue          Ending value of the value range
* @returns                     The corresponding value within the value range
*/
function incrementOverRange(percentValue, startPercent, endPercent, minValue, maxValue) {
    // Limit input range to start/end Percent range
    if (percentValue < startPercent)
        percentValue = startPercent;
    else if (percentValue > endPercent)
        percentValue = endPercent;

    // NOTE: Formula borrowed from Arduino map() function
    return ((percentValue - startPercent) * (maxValue - minValue) / (endPercent - startPercent) + minValue);
}

/**
* Triggers a callback function when the specified element scrolls into range. The callback function is called again once the element scrolls outside of the given range.
*
* Example usage:
*
* scrollTriggeredCallback(".triggerElement", 50, function(triggered) {
*     if (triggered === true)
*         // Do something here 
*     else
*         // Do something else here
* });
*
* NOTE: This version of the function uses the viewport to calculate the scroll position of the element within the screen. 
*       Scroll position = 0% when the element's top boundary is at the bottom of the viewport
*       Scroll position = 100% when the element's bottom boundary is at the top of the viewport
*
* @param {*} triggerElement     The element to trigger the callback on
* @param {*} percentage         Percentage value to trigger the animation
* @param {*} callback           Function to call (with single parameter specfying whether animation was triggered or not) once animation trigger state changes
*                               If animation is triggered, input parameter is set to true and false otherwise.
*/
function scrollTriggeredCallback(triggerElement, percentage, callback) {
    // Get references to the HTML element and its client rect object to work with
    triggerElement = document.querySelector(triggerElement);
    var triggerElementRect = triggerElement.getBoundingClientRect();

    // Calculate the scroll position of the element within the viewport
    let scrollPosition = triggerElementRect.top + triggerElementRect.height; // Sticky height >= viewport height
    let scrollHeight = window.innerHeight + triggerElementRect.height;
    let percentScrolled = 100 - (scrollPosition / scrollHeight) * 100;

    // Limit the scroll range to 0-100%
    if (percentScrolled > 100)
        percentScrolled = 100;
    else if (percentScrolled < 0)
        percentScrolled = 0;

    // Add the animation CSS selector to the given element if the percentScrolled value is within the given percentage range, and remove it otherwise
    if (percentScrolled >= percentage) {
        if(!triggerElement.classList.contains("triggered")) {
            triggerElement.classList.add("triggered");
            callback(true, triggerElement);
        }
    }
    else if (percentScrolled <= 0) {
        if (triggerElement.classList.contains("triggered")) {
            triggerElement.classList.remove('triggered');
            callback(false, triggerElement);
        }
    }
}
#content {
    max-width: 560px;
    margin-left: auto;
    margin-right: auto;
}

span {
    display: block;
    height: 40vh;
    width: 100%;
    background: lightgray;
    margin: 10px 0;
}

.video {
    position: relative;
    width: 100%;
    height: calc(100% / 1.77777);
    display: block;
    cursor: pointer;
}
<html>
    <head>
        <link rel="stylesheet" href="style.css">
        <!--<script defer src="../../Templates/Code Library/Animation Code Library.js"></script>-->
        <script defer src="script.js"></script>
    </head>
    <body>
        <div id="content">
            <span>Text</span>
            <span>Text</span>
            <span>Text</span>
            <span>Text</span>

            <video class="video" controls loading="lazy" style="object-fit: cover">
                <source src="https://www.staging7.midstory.org/wp-content/uploads/2022/01/Video-of-green-foliage.mp4" type="video/mp4">
            </video>
            
            <span>Text</span>
            <span>Text</span>
            <span>Text</span>
            <span>Text</span>
        </div>
    </body>
</html>

javascript html css autoscroll
1个回答
0
投票

实现此目的的一种更有弹性的方法是使用

IntersectionObserver
API,当元素在视口中可见时,它会为您提供回调

例如,你可以尝试这样的事情:

const video = document.querySelector("video");

const observer = new IntersectionObserver(entries => {
  for (const entry of entries) {
    if (entry.isIntersecting) {
      // play video
    } else {
      // pause video
    }
  }
});

observer.observe(video);
© www.soinside.com 2019 - 2024. All rights reserved.