c_k*_*ick 9 javascript css animation easing scroll-snap
我正在使用可滚动元素创建一个图像库。我正在使用 CSS 的scroll-snap
功能,它允许我捕捉到滚动条中的元素(图像)。
通过绑定到元素的scroll
事件,我在用户滚动元素时应用各种操作(例如预加载、隐藏界面元素等)。其中之一取决于滚动事件,并且需要在滚动完成的确切时刻停止。但是滚动快照给我带来了一种无法预见但尚未处理的情况;
我无法准确确定快速滚动操作是否完成。
我可以在每个滚动上设置一个setTimeout
,它会自行取消并重新设置 - 有效地消除抖动 - 如果不重置,最后会被调用。但是设置此值时使用的超时可能意味着在确定滚动完成时“为时已晚”。
底线:如何检查滚动是否完成,因为:
scroll-snap-type
已设置)c_k*_*ick 27
我终于彻底解决了这个脑筋急转弯。解决起来比我最初想象的要简单得多。(注意:在我的例子中,它适用于水平滚动条;在示例中更改offsetWidth
为offsetHeight
和scrollLeft
to以适应垂直滚动条。)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);
Run Code Online (Sandbox Code Playgroud)
通过使用滚动元素自己的宽度(或垂直滚动情况下的高度),我们可以通过将元素的滚动位置(scrollLeft
在我的例子中)除以其宽度(offsetWidth
)来计算它是否已到达其捕捉点,并且如果这会产生一个整数(含义:宽度“适合”滚动位置恰好 x 倍),它已到达捕捉点。我们通过使用余数运算符来做到这一点:
var atSnappingPoint = e.target.scrollLeft % e.target.offsetWidth === 0;
Run Code Online (Sandbox Code Playgroud)
然后,如果到达捕捉点,则将(在滚动完成时应触发的timeOut
中使用)设置为 0。否则,使用常规值(在上面的示例中为 150,请参阅注释)。setTimeout
这是有效的,因为当元素实际到达其捕捉点时,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() {
console.log('Scrolling stopped!');
}, timeOut);
}
myElement.addEventListener('scroll', scrollHandler);
Run Code Online (Sandbox Code Playgroud)
var atSnappingPoint = e.target.scrollLeft % e.target.offsetWidth === 0;
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
滚动超过捕捉点虽然可以通过滚动超过捕捉点来“击中”捕捉点,但是可以通过在设置 时(计算并)考虑滚动速度来缓解这种情况timeOut
,例如,当速度不接近零时将其保持在 150。
像素比:如果计算混乱(1 px 的余数等,因此该函数无法正确解析),则可能存在一些缩放/盒模型问题,这些问题会扰乱滚动位置和 offsetWidth 计算的计算。这很可能是由设备的像素比引起的,因此您可以尝试通过将这些值乘以像素比来“纠正”这些值(向左滚动和宽度)。重要提示:事实证明,像素比不仅表明用户是否拥有高 dpi 屏幕,而且当用户缩放页面时它也会发生变化。
timeOut
滚动尚未达到捕捉点时使用的任意超时时间为 150。这足够长,可以防止在 Safari @ iOS 完成滚动之前触发它(它使用贝塞尔曲线进行滚动捕捉,这会产生很长的“最后一帧”大约 120-130 毫秒)并且足够短,当用户在捕捉点之间暂停滚动时可以产生可接受的结果。
如果您在滚动元素上设置了滚动填充scroll-padding
,则在确定捕捉点时需要考虑到这一点。
剩余像素:您甚至可以进一步分解,以计算到达捕捉点之前剩余的像素:
var pxRemain = e.target.scrollLeft % e.target.offsetWidth;
var atSnappingPoint = pxRemain === 0;
Run Code Online (Sandbox Code Playgroud)
但请注意,您需要从元素的宽度中减去该宽度,具体取决于您滚动的方式。这需要您计算滚动的距离并检查它是负数还是正数。那么它就会变成:
var distance = e.target.scrollLeft - (e.target.prevScrollLeft ? e.target.prevScrollLeft : 0);
var pxRemain = e.target.scrollLeft % e.target.offsetWidth;
pxRemain = (pxRemain === 0) ? 0 : ((distance > 0) ? pxRemain : elementWidth - pxRemain);
var atSnappingPoint = pxRemain === 0;
//store scroll position for next iteration, to calculate distance
e.target.prevScrollLeft = e.target.scrollLeft;
Run Code Online (Sandbox Code Playgroud)
编写此脚本时考虑了两种情况:
如果只需要前者,则不需要超时,直接写:
function scrollHandler(e) {
if (e.target.scrollLeft % e.target.offsetWidth === 0) {
console.log('Scrolling is done!');
}
}
myElement.addEventListener('scroll', scrollHandler);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
8763 次 |
最近记录: |