【问题标题】:stopping mousewheel event from happening twice in OSX阻止鼠标滚轮事件在 OSX 中发生两次
【发布时间】:2014-12-07 05:58:14
【问题描述】:

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

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

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

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

它使用鼠标滚轮 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/ 但这是一个成功/失败。

【问题讨论】:

  • 这是一个类似的问题,似乎已得到解答:stackoverflow.com/questions/17798091/…
  • @sphanley debounce 让它变得更糟。我已经尝试过该帖子中的解决方案,但它并没有真正起作用。
  • 好的!只是觉得它看起来可能是相关的。祝你好运!
  • @sphanley 它肯定是相关的,但解决方案甚至无法接受。我的解决方案效果更好。
  • 如果您能说出问题是否已解决以及提供的答案有多大用处,我将不胜感激。如果没有要求的精确,我们鼓励您给我们更详细的描述。附言网页设计精美。恭喜!

标签: javascript jquery macos scroll


【解决方案1】:

您是否考虑过使用 fullpage.js 代替? 它在到达一个部分和您能够滚动到下一个部分之间存在延迟,这解决了 Mac 用户在使用触控板或 Apple 魔术鼠标时遇到的部分问题。

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

【讨论】:

  • 我也考虑过这个选项。我在最初的研究中看到了这个插件。但在我看来它是锯齿状的,不平滑!但我意识到是附加了#urls 导致速度变慢,我之前也遇到过类似的问题。我禁用了#urls,它是黄油!
  • @salivan 您是否将其与css3:true 选项一起使用?默认设置为 false。这可能是原因。它应该和任何其他的一样快,因为 CSS3 非常流畅。您也可以根据需要编辑缓动。
  • @salivan 如果您觉得它有点乱,请在存储库论坛中打开一个问题以便对其进行审查:github.com/alvarotrigo/fullPage.js/issues
  • 对不起,但是在 mac 上性能不是很好。 css3 也无济于事。当我有时间测试你的插件时,我会打开问题。
  • @salivan 以前没有人报告过这个问题,我家里有一台 Mac,它可以按预期工作。您可能正在使用非常大的图像或其他使其滞后的东西。 The demo site 很适合我。
【解决方案2】:

首先,让我们缩短您的解决方案(因此更容易理解和调试):

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;
}

如您所见,它完全符合您的代码的功能。 但是,我无法理解您的这部分代码(对应于我的 sn-p 中的 (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 => 这不是惯性,您仍然可以触发该事件。

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

【讨论】:

  • 这个问题是mac相关的,在windows上它工作得很好:(无论如何,我不知道Delta是如何计算的,这是mousewheel js插件提供的。
  • @salivan 你确定这是 Mac 唯一的问题吗?您是否在 Windows 笔记本电脑触控板上测试过它?在带有连续滚轮的鼠标上?触摸设备或触摸屏呢?
  • @Alvaro 是的,mac 具有最繁忙的惯性实现。 Windows 鼠标更安静。
  • 嘿,你能看看这个吗? stackoverflow.com/questions/26798580/…
【解决方案3】:

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

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

附:我没有在您的代码中看到任何类似于 eventCallback() 调用的内容(请参阅我的解决方案的 1.2)。只有 fireEvent 标志。您是否正在做类似的事情:

if(fireEvent)
    someEventCallback();

稍后还是什么?

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

更新

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

【讨论】:

  • 请注意,我在答案中的 setTimeout 之前添加了“clearTimeout”行。这很重要,同时它可以防止函数多次触发。即使没有要清除的超时,ClearTimeout 也不会抛出任何错误/警告,这在这种情况下很好。
  • 你在 mac 上吗?如果你能明白我在说什么,那会有所帮助:)
  • @salivan 他提出的建议与 fullPage.js 中实现的完全一样。我也相信这是处理它的唯一方法。它仍然不完美,因为 Mac 具有很强的惯性,如果滚动速度非常快,它可以绕过 500 甚至 600 毫秒的超时,但它解决了 Mac 上正常滚动的问题。
  • @Alvaro 使用你的插件我确实注意到有时会出现超时。我的解决方案实际上避免了这个问题,用户感觉不到。
  • @salivan 不确定您对 我确实注意到有时会出现超时是什么意思。在任何情况下,您都不必使用timeOut,您可以使用计时器,就像pagePiling.js 正在做的那样,甚至one-page-scroll。我不太确定你的解决方案是如何工作的,我会在今天下午进行测试。
【解决方案4】:

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

太好了,正如所承诺的,这是另一种方法。

我们的目标是为 一个 用户操作(在本例中为滚动)提供 一个 响应。

什么是'一次滚动'?为了解决这个问题,假设'一次滚动'是一个从页面开始移动到运动结束的那一刻

动态滚动效果是通过将页面多次移动(例如,每 20 毫秒)一小段距离来实现的。这意味着我们的动态滚动由许多许多小的线性“滚动”组成。

经验测试表明,这种小“滚动”在动能滚动的中间每 17-18 毫秒发生一次,在开始和结束时大约 80-90 毫秒。这是一个我们可以设置的简单测试来查看:

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;

您可以调用所需的回调函数(或事件处理程序)而不是“console.log”,然后就完成了! 该函数只会在每次动能或简单滚动时触发一次,这是我们的目标。

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

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

附注我知道我写的有点多,所以我很乐意回答您的所有问题并在您需要时提供更多代码

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

【讨论】:

  • 也可以设置 prevTime = 0 的初始值。在这种情况下没有太大区别。
  • 这是一个很好的解决方案,但是当用户最终滚动速度较慢时仍然滚动。建议@salivan 考虑加速最大点的解决方案之间的组合怎么样?一旦加速度不再超过它不应该滚动的最大值。
  • @SamBraslavskiy 在我的情况下工作得很好,我用了 50 毫秒,我不确定这是否是所有鼠标滚轮实现的安全赌注:S 无论如何,触摸部分很疯狂,并不真正适用我,sandbox.idev.ge/roomshotel/html5_v3你能看看吗?它在 scrollsections 插件中,处理触摸的部分。
  • @salivan 好吧,是的,它在 iPad 上看起来很乱。如果您还没有阅读过this thread,我建议您阅读(尤其是 geo1701 和 Dave Mackintosh 的答案)。首先尝试他们的解决方案。大约这 50 毫秒...我在我的 mac 上用最新的 chrome 和 safary 测试过,没有错误,但我仍然认为 100 毫秒是最安全的值。
  • @salivan 我真的不想这么说,但归根结底,让本地用户滚动似乎是一个更好的主意......我完全理解这是一个艰难的决定,但我坚信它会提供更好的用户体验。如果有人想访问网站的特定部分,他会点击相应的。左侧的菜单项。如果他滚动,它应该是原生的(没有固定)。您仍然需要跟踪用户滚动了多远才能触发动画和其他操作,但这不是一个大问题(每个视差站点都可以解决它)。想一想……
猜你喜欢
  • 2013-05-04
  • 1970-01-01
  • 2012-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多