是否有可能确定滚动将最终使用javascript?如果有,怎么做呢?
Is it possible to determine where a scroll will end up using javascript? If so, how?
我有一种情况,例如,如果用户的滚动将导致scrollTop改变1000像素,我想提前知道。
一个完美的例子是iCalendar对用户滚动的控制。无论您在iCalendar应用程序中如何滚动,您最多只能滚动到下一个月或前一个月。
我目前有一个非常粗糙的解决方案来限制滚动行为,它只考虑用户当前滚动的位置。
MyConstructor.prototype._stopScroll = function(){
//Cache the previous scroll position and set a flag that will control
//whether or not we stop the scroll
var previous = this._container.scrollTop;
var flag = true;
//Add an event listener that stops the scroll if the flag is set to true
this._container.addEventListener('scroll', function stop(){
if(flag) {
this._container.scrollTop = previous;
}
}.bind(this), false);
//Return a function that has access to the stop function and can remove it
//as an event listener
return function(){
setTimeout(function(){
flag = false;
this._container.removeEventListener('scroll', stop, false);
}.bind(this), 0);
}.bind(this);
};
这个方法是有效的,并且会停止正在进行的滚动,但它不是很顺畅,我想知道是否有更好的方法来完成这个。
这个问题的关键是我能否提前知道滚动将在哪里结束。谢谢! !
编辑:刚刚在github上找到以下项目:
https://github.com/jquery/jquery-mousewheel我尝试了演示,它能够报告我的触摸板和鼠标滚动速度。它也能够停止滚动没有任何位置固定hack:D
我会在接下来的几天里看看,看看我是否可以写一些报告滚动速度,方向,速度,设备等的东西。希望我能做一些jquery插件,可以覆盖所有的滚动交互。
当我有更多关于这个主题的信息时,我会更新这篇文章。
鼠标滚动到哪里是无法预测的。
另一方面,触摸屏/触摸板的滑动具有一定的速度,在用户停止滑动后会减慢,就像汽车受到推动后开始减速一样。
遗憾的是,每个浏览器/操作系统/驱动程序/触摸屏/触摸板等都有自己的减速部分实现,所以我们无法预测。
但是我们当然可以编写我们自己的实现。
我们有三种可以实现的方法:
。方向
B。方向和速度
C。方向、速度和速度
iCalender可能使用实现a
:
向控制台输出滚动方向,用户可以滚动+/- 1px在检测到方向之前。
关于JSFiddle的演示
在JSFiddle上演示动画
(function iDirection() {
var preventLoop = true;
var currentScroll = scrollTop();
function scroll() {
if(preventLoop) {
//Get new scroll position
var newScroll = scrollTop();
//Stop scrolling
preventLoop = false;
freeze(newScroll);
//Check direction
if(newScroll > currentScroll) {
console.log("scrolling down");
//scroll down animation here
} else {
console.log("scrolling up");
//scroll up animation here
}
/*
Time in milliseconds the scrolling is disabled,
in most cases this is equal to the time the animation takes
*/
setTimeout(function() {
//Update scroll position
currentScroll = newScroll;
//Enable scrolling
unfreeze();
/*
Wait 100ms before enabling the direction function again
(to prevent a loop from occuring).
*/
setTimeout(function() {
preventLoop = true;
}, 100);
}, 1000);
}
}
$(window).on("scroll", scroll);
})();
向控制台输出滚动方向、距离和平均速度,用户可以滚动
distance
变量中设置的像素量。如果用户快速滚动,他们可能会滚动几个像素。
关于JSFiddle的演示
(function iDirectionSpeed() {
var distance = 50; //pixels to scroll to determine speed
var preventLoop = true;
var currentScroll = scrollTop();
var currentDate = false;
function scroll() {
if(preventLoop) {
//Set date on scroll
if(!currentDate) {
currentDate = new Date();
}
//Get new scroll position
var newScroll = scrollTop();
var scrolledDistance = Math.abs(currentScroll - newScroll);
//User scrolled `distance` px or scrolled to the top/bottom
if(scrolledDistance >= distance || !newScroll || newScroll == scrollHeight()) {
//Stop scrolling
preventLoop = false;
freeze(newScroll);
//Get new date
var newDate = new Date();
//Calculate time
var time = newDate.getTime() - currentDate.getTime();
//Output speed
console.log("average speed: "+scrolledDistance+"px in "+time+"ms");
/*
To calculate the animation duration in ms:
x: time
y: scrolledDistance
z: distance you're going to animate
animation duration = z / y * x
*/
//Check direction
if(newScroll > currentScroll) {
console.log("scrolling down");
//scroll down animation here
} else {
console.log("scrolling up");
//scroll up animation here
}
/*
Time in milliseconds the scrolling is disabled,
in most cases this is equal to the time the animation takes
*/
setTimeout(function() {
//Update scroll position
currentScroll = newScroll;
//Unset date
currentDate = false;
//Enable scrolling
unfreeze();
/*
Wait 100ms before enabling the direction function again
(to prevent a loop from occuring).
*/
setTimeout(function() {
preventLoop = true;
}, 100);
}, 1000);
}
}
}
$(window).on("scroll", scroll);
})();
输出滚动方向,距离和速度到控制台,用户可以滚动
distance
变量中设置的像素量。如果用户快速滚动,他们可能会滚动几个像素。
关于JSFiddle的演示
(function iDirectionSpeedVelocity() {
var distance = 100; //pixels to scroll to determine speed
var preventLoop = true;
var currentScroll = [];
var currentDate = [];
function scroll() {
if(preventLoop) {
//Set date on scroll
currentDate.push(new Date());
//Set scrollTop on scroll
currentScroll.push(scrollTop());
var lastDate = currentDate[currentDate.length - 1];
var lastScroll = currentScroll[currentScroll.length - 1];
//User scrolled `distance` px or scrolled to the top/bottom
if(Math.abs(currentScroll[0] - lastScroll) >= distance || !lastScroll || lastScroll == scrollHeight()) {
//Stop scrolling
preventLoop = false;
freeze(currentScroll[currentScroll.length - 1]);
//Total time
console.log("Time: "+(lastDate.getTime() - currentDate[0].getTime())+"ms");
//Total distance
console.log("Distance: "+Math.abs(lastScroll - currentScroll[0])+"px");
/*
Calculate speeds between every registered scroll
(speed is described in milliseconds per pixel)
*/
var speeds = [];
for(var x = 0; x < currentScroll.length - 1; x++) {
var time = currentDate[x + 1].getTime() - currentDate[x].getTime();
var offset = Math.abs(currentScroll[x - 1] - currentScroll[x]);
if(offset) {
var speed = time / offset;
speeds.push(speed);
}
}
//Output array of registered speeds (milliseconds per pixel)
console.log("speeds (milliseconds per pixel):");
console.log(speeds);
/*
We can use the array of speeds to check if the speed is increasing
or decreasing between the first and last half as example
*/
var half = Math.round(speeds.length / 2);
var equal = half == speeds.length ? 0 : 1;
var firstHalfSpeed = 0;
for(var x = 0; x < half; x++ ) {
firstHalfSpeed += speeds[x];
}
firstHalfSpeed /= half;
var secondHalfSpeed = 0;
for(var x = half - equal; x < speeds.length; x++ ) {
secondHalfSpeed += speeds[x];
}
secondHalfSpeed /= half;
console.log("average first half speed: "+firstHalfSpeed+"ms per px");
console.log("average second half speed: "+secondHalfSpeed+"ms per px");
if(firstHalfSpeed < secondHalfSpeed) {
console.log("conclusion: speed is decreasing");
} else {
console.log("conclusion: speed is increasing");
}
//Check direction
if(lastScroll > currentScroll[0]) {
console.log("scrolling down");
//scroll down animation here
} else {
console.log("scrolling up");
//scroll up animation here
}
/*
Time in milliseconds the scrolling is disabled,
in most cases this is equal to the time the animation takes
*/
setTimeout(function() {
//Unset scroll positions
currentScroll = [];
//Unset dates
currentDate = [];
//Enable scrolling
unfreeze();
/*
Wait 100ms before enabling the direction function again
(to prevent a loop from occuring).
*/
setTimeout(function() {
preventLoop = true;
}, 100);
}, 2000);
}
}
}
$(window).on("scroll", scroll);
})();
上述实现中使用的辅助函数:
//Source: https://github.com/seahorsepip/jPopup
function freeze(top) {
if(window.innerWidth > document.documentElement.clientWidth) {
$("html").css("overflow-y", "scroll");
}
$("html").css({"width": "100%", "height": "100%", "position": "fixed", "top": -top});
}
function unfreeze() {
$("html").css("position", "static");
$("html, body").scrollTop(-parseInt($("html").css("top")));
$("html").css({"position": "", "width": "", "height": "", "top": "", "overflow-y": ""});
}
function scrollTop() {
return $("html").scrollTop() ? $("html").scrollTop() : $("body").scrollTop();
}
function scrollHeight() {
return $("html")[0].scrollHeight ? $("html")[0].scrollHeight : $("body")[0].scrollHeight;
}
刚刚看了一下评论中提到的滚动,它有10kb,需要挂钩每一个简单的事件:触摸,鼠标滚动,键盘按钮等。
这似乎不是很未来的证明,谁知道未来可能的用户交互会导致滚动?
另一方面,onscroll事件总是在页面滚动时触发,所以让我们把动画代码挂在上面,而不用担心任何输入设备交互。
正如@seahorsepip所说,如果不使用JavaScript添加自定义行为,通常不可能知道滚动将在哪里结束。MDN文档没有列出任何访问队列滚动事件的方法:https://developer.mozilla.org/en-US/docs/Web/Events/scroll
我发现这个信息很有用:规范跨浏览器鼠标滚轮速度
它强调了根据用户输入了解页面走向的困难。我的建议是在代码预测达到阈值时触发滚动到Y事件。在您的示例中,如果滚动在250ms的时间窗口中移动了1000像素的页面800,则将滚动设置为该1000像素标记,并将滚动截断500ms。
https://developer.mozilla.org/en-US/docs/Web/API/window/scrollTo我不太确定我是否有你要找的东西。我曾经有过一个项目,我必须控制滚动。那时我已经覆盖了默认的滚动事件,之后你可以为"一个"滚动设置一个自定义距离。另外增加了滚动到特定位置的jQuery动画。你可以在这里看看:http://c-k.co/zw1/如果这就是你想要的,你可以联系我,我会看看我对自己的东西还有多少了解
很容易使用事件侦听器来实现。下面是一个React的例子:
/**
* scroll promise
*/
const scrollPromiseCallback = useCallback((func:Function) => {
return new Promise((resolve, reject) => {
func(resolve, reject)
})
}, [])
/**
* scroll callback
*/
const scrollCallback = useCallback((scrollContainer, onScrollEnd, resolve) => {
/** 防抖时间 */
const debounceTime = 200
/** 防抖计时器 */
let timer = null
const listener = () => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
scrollContainer.removeEventListener('scroll', listener)
resolve(true)
onScrollEnd?.()
}, debounceTime)
}
scrollContainer.addEventListener('scroll', listener)
}, [])
const scrollTo = useCallback((props:IUseScrollToProps) => {
return scrollPromiseCallback((resolve, reject) => {
const {
scrollContainer = window, top = 0, left = 0, behavior = 'auto',
} = props
scrollCallback(scrollContainer, props?.onScrollEnd, resolve)
scrollContainer.scrollTo({
top,
left,
behavior,
})
})
}, [scrollCallback, scrollPromiseCallback])
- 否则,如果 JavaScript
- 如果javascript中的else导致缺少值
- 如果javascript打开/关闭,则隐藏和显示html代码
- 在'用于'如果javascript中不存在对象属性,则循环
- Don'如果javascript中存在字符,则t匹配
- 如果JavaScript标签是通过谷歌标签管理器插入到页面上的,那么它们的延迟程度是多少;s的自定义HTML
- 如果 JavaScript 返回 true,则重定向到 href
- 如果 JavaScript 错过了 Location.Search,则显示警报消息
- Jquery切换,如果javascript被禁用,则带用户进入页面
- 如果..否则,如果 JavaScript
- 如何单击按钮,如果javascript Onclick()与机械化有关
- 如果javascript不存在,如何告诉css该怎么做
- 如果Javascript被关闭,Braintree.js会发生什么
- 如果javascript事件=true,则启动vbscript
- 如果javascript被禁用,并且您希望在PHP或Regex中使用(XXX)XXX-XXXX,您如何过滤(和替换)电话
- 如果 JavaScript 中的 Else 语句
- 一旦用户开始在文本字段中键入内容,如何选中复选框?如果javascript/html中的textfield为空,也可以取
- 如果javascript代码段未使用,它将阻止其他javascript工作
- 如果javascript中的errors标记为空,则希望重定向用户
- link_to_remote:如果javascript变量为false,我该如何中止操作