jQuery鼠标滚轮:如何检测滚轮何时停止?

24
我正在使用jQuery的mousewheel插件,并且我想检测用户何时已经完成使用鼠标滚轮。 类似于可拖动组件中的stop:事件的功能。 有人可以指引我正确的方向吗?
5个回答

44
这里实际上没有“停止”事件 - 只有当你滚动时才会触发事件,因此每次鼠标滚轮事件发生时都会触发事件...没有任何事件时,处理程序将不会触发。
但是,你可以像这样检测用户是否已经250ms没有使用它:
$("#myElem").mousewheel(function() {
  clearTimeout($.data(this, 'timer'));
  $.data(this, 'timer', setTimeout(function() {
     alert("Haven't scrolled in 250ms!");
     //do something
  }, 250));
});
您可以在此处尝试,我们所做的就是使用 $.data() 在每次使用时存储超时时间,如果在该时间结束之前再次使用,则会清除...否则,您想要运行的任何代码都将被触发,用户已经“完成”了他们想要测试的时间段内的鼠标滚轮使用。请注意保留HTML标签。

是的,我也想到了类似的东西。 我会尝试一下。 非常感谢你,Nick。 - Moustard
1
@Moustard - 欢迎:) 如果问题得到解决,请务必接受答案;) - Nick Craver
2
无法像苹果笔记本电脑或魔术鼠标中的动态滚动一样工作。 - Alvaro

12
完成Nick Craver的回答:

为了完成Nick Craver的回答:

var wheeldelta = {
  x: 0,
  y: 0
};
var wheeling;
$('#foo').on('mousewheel', function (e) {
  if (!wheeling) {
    console.log('start wheeling!');
  }

  clearTimeout(wheeling);
  wheeling = setTimeout(function() {
    console.log('stop wheeling!');
    wheeling = undefined;

    // reset wheeldelta
    wheeldelta.x = 0;
    wheeldelta.y = 0;
  }, 250);

  wheeldelta.x += e.deltaFactor * e.deltaX;
  wheeldelta.y += e.deltaFactor * e.deltaY;
  console.log(wheeldelta);
});

滚动输出:

start wheeling!
Object {x: -1, y: 0}
Object {x: -36, y: 12}
Object {x: -45, y: 12}
Object {x: -63, y: 12}
Object {x: -72, y: 12}
Object {x: -80, y: 12}
Object {x: -89, y: 12}
Object {x: -97, y: 12}
Object {x: -104, y: 12}
Object {x: -111, y: 12}
Object {x: -117, y: 12}
Object {x: -122, y: 12}
Object {x: -127, y: 12}
Object {x: -131, y: 12}
Object {x: -135, y: 12}
Object {x: -139, y: 12}
Object {x: -145, y: 12}
Object {x: -148, y: 12}
Object {x: -152, y: 12}
Object {x: -154, y: 12}
Object {x: -156, y: 12}
Object {x: -157, y: 12}
Object {x: -158, y: 12}
Object {x: -159, y: 12}
Object {x: -161, y: 12}
Object {x: -162, y: 12}
Object {x: -164, y: 12}
Object {x: -166, y: 12}
Object {x: -167, y: 12}
Object {x: -169, y: 12}
stop wheeling!

4

以下是用纯 JavaScript 实现的方法:

var _scrollTimeout = null;

function onMouseWheel() {
    var d = ((typeof e.wheelDelta != "undefined") ? (-e.wheelDelta) : e.detail);
    d = 100 * ((d>0)?1:-1);

    console.log("Scroll delta", d);

    clearTimeout(_scrollTimeout);
    _scrollTimeout = setTimeout(function() {
        console.log("Haven't scrolled in 250ms");
    }, 250);
}

window.addEventListener( 'mousewheel', onMouseWheel, false );
window.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox

2
这是如何实现自己的滚动停止事件的步骤。
//initialise the new variables
var wheelMap = new Map;
var deltaXEnded = false;
var deltaYEnded = false;
var previousSwipe = Object;
    previousSwipe.timeStamp = 0;
    previousSwipe.deltaX = 0;
    previousSwipe.deltaY = 0;
var wheelstart = false;

wheelstop事件创建一个新的事件监听器,我们将从normalWheelEventCallbackFunction()中调用它。
var wheelstop = new Event("wheelstop");

接下来,我们将定义在此事件被分派时的回调函数,然后将该事件添加到 window 对象中。
function wheelstopcallback(event){
    wheelstart = false;
    console.log("wheel event has ended");
}
window.addEventListener("wheelstop", wheelstopcallback.bind(this));

现在我们定义普通的滚轮事件监听器,并定义此监听器将使用的回调函数...
window.addEventListener("wheel", normalWheelEventCallbackFunction.bind(this));

滚轮事件回调函数

function normalWheelEventCallbackFunction(event){
   if(previousSwipe.timeStamp !== 0){
      if(event.timeStamp - previousSwipe.timeStamp < 1000)
         wheelMap.set(event.timeStamp, event);
      else
         wheelMap.clear();
   }
 else{previousSwipe.timeStamp = event.timeStamp;}


  if(event.deltaX > 2 && event.deltaX > previousSwipe.deltaX){
     //forward
     wheelstart = true
  }
  else if(event.deltaX < -2&& event.deltaX < previousSwipe.deltaX){
     //backward
     wheelstart = true;
  }
  else if(event.deltaY > 2 && event.deltaY > previousSwipe.deltaY){
     wheelstart = true;
  }
  else if(event.deltaY < 2 && event.deltaY < previousSwipe.deltaY){
     wheelstart = true;
  }
  
  if(
     ((event.deltaX === 1 || event.deltaX === 0 || event.deltaX === -1) && ((event.deltaX > 0 && event.deltaX < previousSwipe.deltaX) || (event.deltaX < 0 && event.deltaX > previousSwipe.deltaX)) && wheelstart)
     || (wheelstart && (event.deltaX === 0 && previousSwipe.deltaX === 0))
  )
  {
     deltaXEnded = true;
     console.log("deltaXEnded");
  }
  if(
     (((event.deltaY === 1 || event.deltaY === 0 || event.deltaY === -1) && ((event.deltaY > 0 && event.deltaY < previousSwipe.deltaY) || (event.deltaY < 0 && event.deltaY > previousSwipe.deltaY))) && wheelstart)
     || (wheelstart && (event.deltaY === 0 && previousSwipe.deltaY === 0)))     {
        deltaYEnded = true;
        console.log("deltaYEnded");
     }
  
     if(deltaXEnded && deltaYEnded){
        deltaXEnded = false;
        deltaYEnded = false;
        window.dispatchEvent(wheelstop);
     }

  previousSwipe.deltaX = event.deltaX;
  previousSwipe.deltaY = event.deltaY;
}

这段代码可能有一些错误,但大部分逻辑还是比较合理的。如果你需要捕获每个滚轮事件,建议添加备用方案,因为有些事件可能会在“wheelstop”事件之后触发。

最后,请确保实现了处理程序,以防被点击事件打断滚轮事件...

function wheelstopdispatch(){
  if(wheelstart)
    window.dispatchEvent(wheelstop);
  }
window.addEventListener("click", wheelstopdispatch);

1
Nick Craver的答案很好用。但它会导致执行// do something时有小延迟(250毫秒)。更好的选择是立即执行您的代码,然后在等待delay毫秒之后再捕获进一步事件。
为此,请使用一个全局变量,比如说processing,将其初始化为false,并在代码执行前后切换其值。
window.processing = false;
$("#myElem").mousewheel(function() {
   if (processing === false) {
     processing = true;
     // do something
     setTimeout(function() {
       processing = false;
     }, 250)); // waiting 250ms to change back to false.
   }
 });

这并不真正回答问题,也没有提供一个好的(或者“更好的”)替代方案给Tim。举个例子,如果你想在用户停止滚动后显示一个模态框,这个回答会使模态框立即在第一个事件之后出现。请记住,与键盘或鼠标事件不同,滚轮事件是批量处理的,因为例如,用户需要将手指移到滚轮顶部才能继续滚动。你提出的这种方法对于节流来说是有道理的...但并不是OP所问的。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接