摘要
我正在使用一个可滚动的元素创建一个图片库。我正在使用CSS的scroll-snap功能,它允许我在滚动体中的元素(图像)上快速切换。
通过绑定到元素的scroll事件,我将在用户滚动元素(如预装、隐藏接口元素等)时应用各种操作。其中一个依赖于滚动事件,需要在滚动完成的准确时刻停止。但是,卷轴抓拍给我带来了一种不可预见的,但仍未处理的情况;
如果快速滚动操作完成,I 无法准确地确定。
我可以在每个卷轴上设置一个setTimeout,它会取消自身并重新设置--有效地取消设置--如果没有重置,最终也会被调用。但设置此值时所使用的超时,可能意味着在确定滚动完成时“为时已晚”。
底线:如何检查滚动是否完成,因为:
scroll-snap-type设置)发布于 2021-02-03 14:34:28
我终于彻底解决了这个脑残问题。这比我原先想的要简单得多。(注意:在我的示例中,它用于水平滚动体;在示例中将offsetWidth更改为offsetHeight,将scrollLeft更改为scrollTop,以适应垂直滚动体。)
function scrollHandler(e) {
var atSnappingPoint = e.target.scrollLeft % e.target.offsetWidth === 0;
var timeOut = atSnappingPoint ? 0 : 150; //see notes
clearTimeout(e.target.scrollTimeout); //clear previous timeout
e.target.scrollTimeout = setTimeout(function() {
console.log('Scrolling stopped!');
}, timeOut);
}
myElement.addEventListener('scroll', scrollHandler);细目
通过使用滚动元素自己的宽度(或垂直滚动的情况下的高度),我们可以通过将元素的滚动位置(在我的例子中为scrollLeft)除以其宽度(offsetWidth)来计算它是否已经达到其快照点,如果这将产生一个圆形的整数(意思是:宽度‘适合’滚动位置的精确x倍),那么它已经到达了快照点。我们通过使用余算子来做到这一点。
var atSnappingPoint = e.target.scrollLeft % e.target.offsetWidth === 0;然后,如果到达快照点,则将timeOut (在滚动完成后应该触发的setTimeout中使用)设置为0。否则,将使用常规值(在上面的示例150中,请参见注释)。
这是因为当元素实际到达其快照点时,会触发最后一个scroll事件,而我们的处理程序也会被触发(再次)。将timeOut调整为0将立即调用 (见mdn)调用超时函数;因此,当滚动器“命中”快照点时,我们立即知道这一点。
演示
下面是一个有用的例子:
function scrollHandler(e) {
var atSnappingPoint = e.target.scrollLeft % e.target.offsetWidth === 0;
var timeOut = atSnappingPoint ? 0 : 150; //see notes
clearTimeout(e.target.scrollTimeout); //clear previous timeout
e.target.scrollTimeout = setTimeout(function() {
//using the timeOut to evaluate scrolling state
if (!timeOut) {
console.log('Scroller snapped!');
} else {
console.log('User stopped scrolling.');
}
}, timeOut);
}
myElement = document.getElementById('scroller');
myElement.addEventListener('scroll', scrollHandler);.scroller {
display: block;
width: 400px;
height: 100px;
overflow-scrolling: touch;
-webkit-overflow-scrolling: touch;
overflow-anchor: none;
overflow-x: scroll;
overflow-y: hidden;
scroll-snap-type: x mandatory;
scroll-snap-stop: normal;
scroll-behavior: auto;
}
.scroller-canvas {
position: relative;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
.scroller-canvas > * {
position: relative;
display: inline-flex;
flex-flow: column;
flex-basis: 100%;
flex-shrink: 0;
width: 400px;
height: 100px;
scroll-snap-align: start;
scroll-snap-stop: normal;
}
.scroller-canvas > *:nth-child(even) {
background-color: #666;
color: #FFF;
}
/* stackoverflow code wrapper fix */
.as-console-wrapper { max-height: 50px !important; }<div class="scroller" id="scroller">
<div class="scroller-canvas">
<div class="slide" id="0">Slide 1</div>
<div class="slide" id="1">Slide 2</div>
<div class="slide" id="2">Slide 3</div>
<div class="slide" id="3">Slide 4</div>
<div class="slide" id="4">Slide 5</div>
</div>
</div>
备注
在快照点上滚动--我还没有实现/考虑到这样一个事实:您可以通过滚动它来“击中”快照点。不过,这是非常罕见的。当我找到解决方案时,我会更新答案。
像素比:如果您得到了错误的计算(1 px的剩余部分,因此函数没有正确解决),您可能会遇到一些缩放/框模型问题,这些问题会使滚动位置的计算和offsetWidth的计算都搞砸。这是不可能的,这是由设备的像素比,所以你可以尝试‘纠正’的值(滚动左和宽度)乘以这些像素比。重要的是:像素比不仅表示用户是否有高dpi屏幕,而且当用户缩放页面时,它也会发生变化。
当滚动尚未达到断点时使用的超时值为150。这足以防止它在Safari @ iOS完成滚动之前被触发(它使用bezier曲线来滚动抓取,产生一个非常长的“最后一帧”约为120-130 is ),足够短到当用户暂停在快照点之间滚动时产生可接受的结果。
scroll-padding如果在滚动元素上设置了scroll-padding,则在确定快照点时需要考虑到这一点。
像素剩余:您甚至可以进一步细分,在到达快照点之前计算出剩余的像素:
var pxRemain = e.target.scrollLeft % e.target.offsetWidth;
var atSnappingPoint = pxRemain === 0;但是请注意,您需要从元素的宽度中减去这个值,这取决于滚动的方式。这需要计算滚动的距离,并检查是负的还是正的。然后,它将变成:
var pxRemain = e.target.scrollLeft % e.target.offsetWidth;
pxRemain = (pxRemain === 0) ? 0 : ((distance > 0) ? pxRemain : elementWidth - pxRemain);
var atSnappingPoint = pxRemain === 0;只抓拍
编写此脚本时考虑到了两种情况:
如果只需要前者,则不需要超时,只需编写:
function scrollHandler(e) {
if (e.target.scrollLeft % e.target.offsetWidth === 0) {
console.log('Scrolling is done!');
}
}
myElement.addEventListener('scroll', scrollHandler);https://stackoverflow.com/questions/65952068
复制相似问题