使用去抖滚动事件固定元素以提高性能
Pinning Elements with Debounced Scroll Event for Performance
根据滚动位置平滑固定元素的正确方法是什么?
我尝试对滚动侦听器进行去抖动以提高性能,但固定不准确。即使将 debouncing 设置为 10 毫秒,它也不平滑,并且元素不会干净地捕捉到其初始位置。
var scrolling = false;
var stickPosY = 100;
var heights = [];
$(".element").each( function(index) {
heights[index] = $(".element[data-trigger=" + index + "]").offset().top;
});
function pin() {
if ( !$("#aside").hasClass("fixed") ) {
var stickyLeft = $("#aside").offset().left;
var stickyWidth = $("#aside").outerWidth();
var stickyTop = $("#aside").offset().top - stickPosY;
$("#aside").addClass("fixed");
$("#aside").css({"left": stickyLeft, "top": stickyTop, "width": stickyWidth});
}
}
function unpin() {
$("#aside").css({"left": "", "top": "", "width": ""});
$("#aside").removeClass("fixed")
}
$( window ).scroll( function() {
scrolling = true;
});
setInterval( function() {
if ( scrolling ) {
scrolling = false;
var y = window.scrollY;
console.log(y);
// PIN SIDEBAR
y > stickPosY ? pin() : unpin();
//TRIGGERS
for (var i=0; i < heights.length; i++) {
if (y >= heights[i]) {
$('.element[data-trigger="' + i + '"]').addClass("blue");
}
else {
$('.element[data-trigger="' + i + '"]').removeClass("blue");
}
}
}
}, 250 );
这是我的 Pen
我尝试在带有图钉和其他触发器的场景中为项目使用 scrollMagic,但滚动不是很流畅。所以我试图用精简版和去抖动的听众重建它。这种方法可行吗,还是我应该尝试优化我的 scrollMagic 场景?
你可以试试 fixed or sticky CSS positioning:
#element {
position: fixed;
top: 80px;
left: 10px;
}
位置:固定将使元素始终保持在距顶部 80 像素和距左边缘 10 像素的位置,无论滚动位置如何。
#element{
position: sticky;
top: 0;
right: 0;
left: 0;
}
这是我的一个项目。该元素是一个导航栏。它位于 header 栏下方,因此当您位于页面顶部时,您会看到 header,然后是其下方的导航栏,当您向下滚动时,header 移开屏幕,但导航固定在顶部并且始终可见。
正如 James 指出的那样,您可以只使用 position: sticky
作为一个选项,但这在旧浏览器中不起作用,并且它的使用仅限于较新浏览器中的简单情况,所以我将继续假设您想走那条路的 JS 解决方案。
你的 JS 中发生了很多事情,我认为你可能把事情搞得太复杂了,所以我会给你一些基础知识供你考虑。
当您基于滚动切换内容时,切换内联样式或 class,但不能同时切换。我建议切换 class,因为它允许您拥有一个可以在多种屏幕尺寸上工作的功能(即,您可以使用媒体查询来根据屏幕尺寸更改切换 class 的行为) .它还将所有样式保存在一个地方,而不是让它们在 JS 和样式表之间拆分。
尽量减少滚动时正在做的工作。例如,缓存对滚动函数外部变量中元素的引用,这样您就不会在每次滚动像素时都不断查找它们。避免在滚动函数内循环。
通常不推荐使用 setInterval
来提高滚动功能的性能。所有要做的就是 运行 每 X 时间执行一次函数,无论您是否滚动。您真正想做的是直接对滚动功能进行速率限制。这样,如果您真正快速地滚动很长一段距离,您的函数将只会被调用它本来会被调用的总次数的一小部分,但是如果您缓慢地滚动一小段距离,它仍然会被调用最少次数以保持东西看起来很流畅,如果你根本不滚动,那么你根本就没有调用你的函数。此外,在这种情况下,您可能希望节流您的函数,而不是去抖动它。
考虑使用 Underscore.js 或 Lodash.js 中的 throttle 函数,而不是发明你自己的,因为这些函数具有高性能并且保证可以在各种浏览器上工作。
这是一个简单的示例,它在滚动时将一个元素粘贴到屏幕顶部,并使用 Lodash 进行了限制。我使用的是 25 毫秒的节流阀,这大约是我建议的最大量,以保持事情看起来流畅,当您滚动超过阈值时,您不会真正注意到元素 sticking/unsticking 的延迟。您可以减少到 10 毫秒。
$(function() {
$(window).on('scroll', _.throttle(toggleClass, 25));
const myThing = $('#my-thing');
const threshold = $('#dummy-1').height();
function toggleClass() {
const y = window.scrollY;
if (y > threshold) {
myThing.addClass('stuck')
} else {
myThing.removeClass('stuck');
}
}
});
#dummy-1 {
height: 150px;
background-color: steelblue;
}
#dummy-2 {
height: 150px;
background-color: gold;
}
#my-thing {
width: 300px;
height: 75px;
background-color: firebrick;
position: absolute;
top: 150px;
left: 0;
}
#my-thing.stuck {
position: fixed;
top: 0;
}
body {
margin: 0;
height: 2000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.0.0/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="dummy-1"></div>
<div id="dummy-2"></div>
<div id="my-thing"></div>
根据滚动位置平滑固定元素的正确方法是什么? 我尝试对滚动侦听器进行去抖动以提高性能,但固定不准确。即使将 debouncing 设置为 10 毫秒,它也不平滑,并且元素不会干净地捕捉到其初始位置。
var scrolling = false;
var stickPosY = 100;
var heights = [];
$(".element").each( function(index) {
heights[index] = $(".element[data-trigger=" + index + "]").offset().top;
});
function pin() {
if ( !$("#aside").hasClass("fixed") ) {
var stickyLeft = $("#aside").offset().left;
var stickyWidth = $("#aside").outerWidth();
var stickyTop = $("#aside").offset().top - stickPosY;
$("#aside").addClass("fixed");
$("#aside").css({"left": stickyLeft, "top": stickyTop, "width": stickyWidth});
}
}
function unpin() {
$("#aside").css({"left": "", "top": "", "width": ""});
$("#aside").removeClass("fixed")
}
$( window ).scroll( function() {
scrolling = true;
});
setInterval( function() {
if ( scrolling ) {
scrolling = false;
var y = window.scrollY;
console.log(y);
// PIN SIDEBAR
y > stickPosY ? pin() : unpin();
//TRIGGERS
for (var i=0; i < heights.length; i++) {
if (y >= heights[i]) {
$('.element[data-trigger="' + i + '"]').addClass("blue");
}
else {
$('.element[data-trigger="' + i + '"]').removeClass("blue");
}
}
}
}, 250 );
这是我的 Pen
我尝试在带有图钉和其他触发器的场景中为项目使用 scrollMagic,但滚动不是很流畅。所以我试图用精简版和去抖动的听众重建它。这种方法可行吗,还是我应该尝试优化我的 scrollMagic 场景?
你可以试试 fixed or sticky CSS positioning:
#element {
position: fixed;
top: 80px;
left: 10px;
}
位置:固定将使元素始终保持在距顶部 80 像素和距左边缘 10 像素的位置,无论滚动位置如何。
#element{
position: sticky;
top: 0;
right: 0;
left: 0;
}
这是我的一个项目。该元素是一个导航栏。它位于 header 栏下方,因此当您位于页面顶部时,您会看到 header,然后是其下方的导航栏,当您向下滚动时,header 移开屏幕,但导航固定在顶部并且始终可见。
正如 James 指出的那样,您可以只使用 position: sticky
作为一个选项,但这在旧浏览器中不起作用,并且它的使用仅限于较新浏览器中的简单情况,所以我将继续假设您想走那条路的 JS 解决方案。
你的 JS 中发生了很多事情,我认为你可能把事情搞得太复杂了,所以我会给你一些基础知识供你考虑。
当您基于滚动切换内容时,切换内联样式或 class,但不能同时切换。我建议切换 class,因为它允许您拥有一个可以在多种屏幕尺寸上工作的功能(即,您可以使用媒体查询来根据屏幕尺寸更改切换 class 的行为) .它还将所有样式保存在一个地方,而不是让它们在 JS 和样式表之间拆分。
尽量减少滚动时正在做的工作。例如,缓存对滚动函数外部变量中元素的引用,这样您就不会在每次滚动像素时都不断查找它们。避免在滚动函数内循环。
通常不推荐使用
setInterval
来提高滚动功能的性能。所有要做的就是 运行 每 X 时间执行一次函数,无论您是否滚动。您真正想做的是直接对滚动功能进行速率限制。这样,如果您真正快速地滚动很长一段距离,您的函数将只会被调用它本来会被调用的总次数的一小部分,但是如果您缓慢地滚动一小段距离,它仍然会被调用最少次数以保持东西看起来很流畅,如果你根本不滚动,那么你根本就没有调用你的函数。此外,在这种情况下,您可能希望节流您的函数,而不是去抖动它。考虑使用 Underscore.js 或 Lodash.js 中的 throttle 函数,而不是发明你自己的,因为这些函数具有高性能并且保证可以在各种浏览器上工作。
这是一个简单的示例,它在滚动时将一个元素粘贴到屏幕顶部,并使用 Lodash 进行了限制。我使用的是 25 毫秒的节流阀,这大约是我建议的最大量,以保持事情看起来流畅,当您滚动超过阈值时,您不会真正注意到元素 sticking/unsticking 的延迟。您可以减少到 10 毫秒。
$(function() {
$(window).on('scroll', _.throttle(toggleClass, 25));
const myThing = $('#my-thing');
const threshold = $('#dummy-1').height();
function toggleClass() {
const y = window.scrollY;
if (y > threshold) {
myThing.addClass('stuck')
} else {
myThing.removeClass('stuck');
}
}
});
#dummy-1 {
height: 150px;
background-color: steelblue;
}
#dummy-2 {
height: 150px;
background-color: gold;
}
#my-thing {
width: 300px;
height: 75px;
background-color: firebrick;
position: absolute;
top: 150px;
left: 0;
}
#my-thing.stuck {
position: fixed;
top: 0;
}
body {
margin: 0;
height: 2000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.0.0/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="dummy-1"></div>
<div id="dummy-2"></div>
<div id="my-thing"></div>