You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript实现视频自动滚动时取消滚动惯性问题排查

问题:快速滚动时自动滚动视频居中出现偏移

当前代码在视频进入视口25%时自动滚动到视频居中位置,但快速滚动时,页面滚动惯性会导致自动滚动过度偏移,无法准确停在视频居中位置。

解决思路

要抵消滚动惯性,核心是:

  • 触发自动滚动时立即锁定原生滚动,避免惯性继续影响
  • 自动滚动完成后恢复原生滚动
  • 实时修正目标位置,避免因惯性导致的初始位置偏差

修改步骤与完整代码

1. 核心修改说明

  • 添加全局状态变量isAutoScrolling标记是否正在执行自动滚动,阻止原生滚动事件干扰
  • 在scrollToElement中实时计算目标位置,抵消惯性带来的元素位置变化
  • 动画结束后强制修正到准确目标位置,确保居中效果

修改后的完整代码

JavaScript

let isAutoScrolling = false; // 滚动锁定状态

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

window.addEventListener("scroll", () => {
    if (isAutoScrolling) return; // 自动滚动时跳过原生滚动处理

    const videoScrollSpeed = 1000;
    const videoScrollTriggerPercentage = 25;
    const video = "video";
    
    scrollTriggeredCallback(video, videoScrollTriggerPercentage, function(trigger, triggerElement){
        if (trigger === true) {
            scrollToElement(triggerElement, videoScrollSpeed, function(placeholder) {
                console.log ("Scroll complete");
            });
        }
    });
});


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

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);
    }

    isAutoScrolling = true; // 标记开始自动滚动

    // 实时获取目标位置,抵消惯性导致的偏差
    const getTargetPosition = () => {
        const currentRect = element.getBoundingClientRect();
        const absoluteElementTop = currentRect.top + window.pageYOffset;
        return absoluteElementTop - (window.innerHeight / 2) + (currentRect.height / 2);
    };

    const startPosition = doc.scrollTop;
    let targetPosition = getTargetPosition();

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

        const elapsed = timestamp - startTime;
        targetPosition = getTargetPosition(); // 实时更新目标位置
    
        if (previousTimeStamp !== timestamp) {
            doc.scrollTop = incrementOverRange(startTime + elapsed, startTime, startTime + duration, startPosition, targetPosition); 
        }
    
        if (elapsed < duration) {
            previousTimeStamp = timestamp;
            window.requestAnimationFrame(stepScroll);
        } else {
            doc.scrollTop = targetPosition; // 强制修正到准确位置
            isAutoScrolling = false; // 解锁滚动
            if (callback) {
                callback(element);
            }
        }
    }

    window.requestAnimationFrame(stepScroll);
}

function getCSSTranslationOffset(element) {
    element = getDOMObject(element);
    const computedStyle = window.getComputedStyle(element);
    const transformMatrix = new DOMMatrix(computedStyle.transform);
    return [transformMatrix.m41, transformMatrix.m42]
}

function incrementOverRange(percentValue, startPercent, endPercent, minValue, maxValue) {
    if (percentValue < startPercent)
        percentValue = startPercent;
    else if (percentValue > endPercent)
        percentValue = endPercent;

    return ((percentValue - startPercent) * (maxValue - minValue) / (endPercent - startPercent) + minValue);
}

function scrollTriggeredCallback(triggerElement, percentage, callback) {
    triggerElement = document.querySelector(triggerElement);
    var triggerElementRect = triggerElement.getBoundingClientRect();

    let scrollPosition = triggerElementRect.top + triggerElementRect.height;
    let scrollHeight = window.innerHeight + triggerElementRect.height;
    let percentScrolled = 100 - (scrollPosition / scrollHeight) * 100;

    if (percentScrolled > 100)
        percentScrolled = 100;
    else if (percentScrolled < 0)
        percentScrolled = 0;

    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);
        }
    }
}

CSS

#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

<html>
    <head>
        <link rel="stylesheet" href="style.css">
        <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>

内容的提问来源于Stack Exchange,提问作者Jason O

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 10:43:07