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