在OSX中停止鼠标轮事件发生两次

时间:2014-10-12 15:49:28

标签: javascript jquery macos scroll inertia

我注意到鼠标轮事件在mac osx中多次发生。可以归因于惯性特征。

有没有办法解决这种问题?

(自签名ssl请不要担心!) https://sandbox.idev.ge/roomshotel/html5_v2/

我正在使用scrollSections.js https://github.com/guins/jQuery.scrollSections

它使用mousewheel jquery插件:https://github.com/brandonaaron/jquery-mousewheel

我看到很多人遇到同样的问题:https://github.com/brandonaaron/jquery-mousewheel/issues/36

有一些解决方案,但没有一个与scrollSections插件一起使用。

如何从JS中禁用此惯性功能?

我的尝试修复:

// Fix for OSX inertia problem, jumping sections issue.
if (isMac) {

  var fireEvent;
  var newDelta = deltaY;

  if (oldDelta != null) {

    //check to see if they differ directions
    if (oldDelta < 0 && newDelta > 0) {
      fireEvent = true;
    }

    //check to see if they differ directions
    if (oldDelta > 0 && newDelta < 0) {
      fireEvent = true;
    }

    //check to see if they are the same direction
    if (oldDelta > 0 && newDelta > 0) {

      //check to see if the new is higher
      if (oldDelta < newDelta) {
        fireEvent = true;
      } else {
        fireEvent = false;
      }
    }

    //check to see if they are the same direction
    if (oldDelta < 0 && newDelta < 0) {

      //check to see if the new is lower
      if (oldDelta > newDelta) {
        fireEvent = true;
      } else {
        fireEvent = false;
      }
    }

  } else {

    fireEvent = true;

  }

  oldDelta = newDelta;

} else {

  fireEvent = true;

}

你可以看到在这里实施的修复:https://sandbox.idev.ge/roomshotel/html5_v2/但它是一个命中/未命中。

4 个答案:

答案 0 :(得分:10)

带有超时的最新解决方案有一个主要缺点:动态滚动效果可能持续相当长(甚至1秒左右)......并且禁用滚动1-2秒并不是最好的决定。

Soooo,正如所承诺的,这是另一种方法。

我们的目标是为一个用户操作提供一个响应,在这种情况下是滚动。

&#39;一个滚动&#39; 为了解决这个问题,让我们说&#39;一个滚动&#39; 39;是从页面开始移动到移动结束的那一刻持续的事件

动态滚动效果是通过将页面移动多次(例如,每20ms)一小段距离来实现的。这意味着我们的动态滚动包含许多很小的线性滚动&#39;。

经验测试表明,这个小小的滚动&#39;在动能滚动中间每17-18ms发生一次,在开始和结束时约80-90ms。这是我们可以设置的简单测试:

var oldD;
var f = function(){
    var d = new Date().getTime();
    if(typeof oldD !== 'undefined')
        console.log(d-oldD);
    oldD = d;
}
window.onscroll=f;

重要!每次发生此小型滚动时,都会触发滚动事件。所以:

 window.onscroll = function(){console.log("i'm scrolling!")};
在一次动力学滚动中,

将被触发15到20次。顺便说一下,onscroll有很好的浏览器支持(参见compatibility table),所以我们可以依赖它(触摸设备除外,我稍后会讨论这个问题);

有人可能会说重新定义window.onscroll不是设置事件侦听器的最佳方法。是的,我们鼓励您使用

 $(window).on('scroll',function(){...});

或者你喜欢什么,它不是问题的关键(我个人使用我自己编写的库)。

所以,在onscroll事件的帮助下,我们可以可靠地说出页面的这个特殊的迷你动作是属于一个持久的动态滚动,还是一个新的:

    var prevTime = new Date().getTime();
    var f = function(){
        var curTime = new Date().getTime();
        if(typeof prevTime !== 'undefined'){
            var timeDiff = curTime-prevTime;
            if(timeDiff>200)
                console.log('New kinetic scroll has started!');
        }
        prevTime = curTime;
    }
    window.onscroll=f;

而不是&#34; console.log&#34;你可以调用你想要的回调函数(或事件处理程序),你就完成了! 这个函数只会在每个动力学或简单的滚动上被触发一次,这是我们的目标。

您可能已经注意到我已经使用了200毫秒作为它是新卷轴还是前一卷轴的一部分的标准。您可以将其设置为更高的值为999%,确保您可以阻止任何额外的呼叫。但请注意,我们在之前的回答中使用了 NOT 。它只是两个页面移动之间的一段时间(无论是新卷轴还是动态卷轴的一小部分)。在我看来,在动能滚动的步骤之间会有一个超过200毫秒的延迟(否则根本不会是平滑的)。

正如我上面提到的,onscroll事件在触摸设备上的工作方式不同。它在动能滚动的每一个小步骤中都不会起火。但是当页面的移动最终结束时它会触发。此外,还有触及事件......所以,这不是什么大不了的事。如有必要,我也可以为触摸设备提供解决方案。

<强> P.S。我知道我写得太多了,所以如果您需要一个,我很乐意回答您的所有问题,并提供更多代码。

所有浏览器都支持所提供的解决方案,非常轻量级,更重要的是,它不仅适用于mac,而且适用于可能实现动态滚动的每个设备,所以我认为它真的是一个要走的路。

答案 1 :(得分:4)

你知道,我认为在这种情况下使用超时是一个更好的主意。为什么不写这样的东西:

// Let's say it's a global context or whatever...:
var fireEvent = true;
var newDelta, oldDelta, eventTimeout;
newDelta = oldDelta = eventTimeout = null;

// ... and the function below fires onmousewheel or anything similar:
function someFunc(){
    if(!fireEvent) return; // if fireEvent is not allowed => stop execution here ('return' keyword stops execution of the function), else, execute code below:
    newDelta = deltaY;
    if(oldDelta!=null&&oldDelta*newDelta>0){ // (1.1) if it's not the first event and directions are the same => prevent possible dublicates for further 50ms:
        fireEvent = false;
        clearTimeout(eventTimeout); // clear previous timeouts. Important!
        eventTimeout = setTimeout(function(){fireEvent = true},500);
    }
    oldDelta = newDelta;
    someEventCallback(); // (1.2) fire further functions...
    }

因此,任何前一次鼠标滚轮事件调用后半秒内触发的任何鼠标滚轮事件都将被忽略,如果它与之前的方向相同(参见1.1中的条件)。它将解决这个问题并没有用户会发现这一点。延迟金额可能会更改,以更好地满足您的需求。

解决方案是在纯JS上完成的。欢迎您提出有关将其集成到您的环境中的任何问题,但我们需要您提供更多页面代码。

P.S。我没有在代码中看到类似于eventCallback()的调用(参见我的解决方案的1.2)。只有fireEvent标志。你做的事情是这样的:

if(fireEvent)
    someEventCallback();

以后还是什么?

P.P.S.note注意,fireEvent应该在全局范围内,以便在这里使用setTimeout。如果不是,它也很容易让它工作正常,但代码需要稍微修改一下。如果是您的情况,请告诉我,我会为您解决。

<强>更新

经过短暂的搜索后我发现,在Underscore的_debounce()函数中使用了类似的机制。请参阅下划线文档here

答案 2 :(得分:1)

您是否考虑过使用 fullpage.js ? 它到达一个部分和你能够滚动到下一部分之间有一个延迟,它解决了Mac用户使用履带板或Apple魔术鼠标的部分问题。

它还可以为您提供一些其他好处,例如更多选项,方法以及与没有CSS3支持的触摸设备和旧浏览器的兼容性。

答案 3 :(得分:0)

要开始使用,请让您的解决方案更短(因此更容易理解和调试):

var fireEvent;
var newDelta = deltaY;
var oldDelta = null;

fireEvent = EventCheck();
oldDelta = newDelta;

function EventCheck(){
    if(oldDelta==null) return true; //(1.1)
    if(oldDelta*newDelta < 0) return true; // (1.2) if directions differ => fire event
    if(Math.abs(newDelta)<Math.abs(oldDelta)) return true; // (1.3) if oldDelta exceeds newDelta in absolute values => fire event
    return false; // (1.4) else => don't fire;
}

如您所见,它绝对完全符合您的代码。 但是,我无法理解您的代码的这一部分(与我的代码段中的(1.3)相对应):

 //check to see if the new is lower
 if (oldDelta > newDelta) {
    fireEvent = true;
 } else {
    fireEvent = false;
 }

从代码中可以看出它是如何计算deltaY的。可以假设,delta等于endPosition - initialPosition。因此,oldDelta&gt; newDelta并不意味着新的位置较低,而是这两个值之间的新差距更大。如果它的含义是什么,你仍然使用它,我想你试着跟踪它的惯性。那么你应该改变比较运算符(使用小于,而不是大于,反之亦然)。换句话说,我写道:

if(Math.abs(newDelta)>Math.abs(oldDelta)) return true; // (1.3) 

你知道,现在我已经使用了大于&#39;运算符,表示:newDelta绝对值超过oldDelta =&gt;它没有惯性,你仍然可以开火。

这是您尝试实现的目标还是我误解了您的代码?如果是这样,请解释如何计算deltaY以及比较旧的和新的Deltas的目标是什么。 附:我建议不要在这一步中使用if(isMac),而问题也可能隐藏在那里。