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

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

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

(自签名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禁用这个惯性function?

我试图修复:

// 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) { fireEvent = true; } //check to see if they differ directions if (oldDelta > 0 && newDelta  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  newDelta) { fireEvent = true; } else { fireEvent = false; } } } else { fireEvent = true; } oldDelta = newDelta; } else { fireEvent = true; } 

您可以在此处看到修复程序: https : //sandbox.idev.ge/roomshotel/html5_v2/但它是一个命中/未命中。

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

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

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

什么是‘滚动’? 为了解决这个问题,我们假设“一次滚动”是一个从页面开始移动到移动结束的那一刻持续的事件

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

经验测试表明,这种小“滚动”在动能滚动中间每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有很好的浏览器支持(参见兼容性表 ),所以我们可以依赖它(触摸设备除外,我稍后会讨论这个问题);

有人可能会说重新定义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; 

而不是“console.log”,你可以调用你想要的回调函数(或事件处理程序),你就完成了! 每次动能或简单滚动时,该function仅被触发一次,这是我们的目标。

您可能已经注意到我使用了200ms作为新卷轴或前一卷轴的一部分的标准。 您可以将其设置为更高的值,即999%,确保您可以阻止任何额外的呼叫。 但是,请记住,这不是我之前的答案中使用的内容。 它只是两个页面移动之间的一段时间(无论是新卷轴还是动态卷轴的一小部分)。 在我看来,在动能滚动的步骤之间存在超过200ms的滞后的可能性很小(否则它将完全不平滑)。

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

PS我明白我写得太多了,所以我很乐意回答你的所有问题,如果你需要的话还可以提供更多代码

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

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

 // 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上完成的。 欢迎您提出有关在您的环境中集成它的任何问题,但我会要求您提供更多页面代码。

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

 if(fireEvent) someEventCallback(); 

以后还是什么?

PPSnote表示fireEvent应该在全局范围内,以便在这里使用setTimeout。 如果不是,它也很容易使它工作正常,但代码需要改变一点。 如果是你的情况,请告诉我,我会为你解决。

UPDATE

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

你有没有关于使用fullpage.js ? 它到达一个部分和你能够滚动到下一部分之间的延迟,这解决了Mac用户使用跟踪垫或Apple魔术鼠标的部分问题。

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

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

 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) 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> newDelta并不意味着新的位置较低,但这两个值之间的新差距更大。 如果这是它的意思,你仍然使用它,我想你试图跟踪惯性。 然后你应该改变比较运算符(使用小于,而不是大于,反之亦然)。 换句话说,我写道:

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

你看,现在我使用了’大于’运算符,这意味着:newDelta绝对值超过oldDelta =>它不是惯性,你仍然可以触发事件。

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