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
相关产品推荐
相关产品推荐

