jQuery: 减少触发 mousemove 事件的频率

9
我正在尝试找到一种干净的方法来聚合mousemove事件,以确保我的代码被调用,但每250-300毫秒只调用一次。
我考虑使用以下内容之类的东西,但想知道是否有更好的模式或jQuery提供的相同功能:
var mousemove_timeout = null;

$('body').mousemove(function() {
  if (mousemove_timeout == null) {
    mousemove_timeout = window.setTimeout(myFunction, 250);
  }
});

function myFunction() {
  /*
   * Run my code...
   */

  mousemove_timeout = null;
}

编辑:下面被接受的答案适用于此情况,但是我发现答案中提供的mousestop()功能实际上消除了我对聚合的需求。 因此,如果您正在阅读本问题并寻找答案,请查看mousestop插件是否是您真正需要的!

9个回答

20

我尝试了被采纳的答案中的解决方案之后,发现如果鼠标持续移动,特别是进行圆形运动时,mousemove()事件会不断触发,但鼠标坐标保持不变。 因此,我想出了一个更简单的解决方案,消除了mousestop()和setTimeout。

$("body").mousemove(function (e) {
        if (enableHandler) {
            handleMouseMove(e);
            enableHandler = false;
        }
});

timer = window.setInterval(function(){
    enableHandler = true;
}, 100);

这将大约每100毫秒正确调用handleMouseMove()。(请注意,我说的是大约,因为JavaScript中的时间延迟和间隔并非实时保证)


1
+1 - 谢谢!这实际上是我最初寻找的解决方案类型。这比被接受的答案更为简洁,但是我仍然选择了mousestop插件。 - Topher Fangio
当鼠标不移动时,有没有办法停止计时器? - Mokhlesur Rahman

5

您的代码很好,只是在将其设置为null之前,应该清除超时,否则可能会泄漏:

window.clearTimeout(mousemove_timeout);
mousemove_timeout = null;

作为替代方案,您可以使用mousemove/mousestopwindow.setInterval结合使用。
var timer = null;
var isIntervalSet = false;

$('body').mousemove(function() {
    if (isIntervalSet) {
        return;
    }
    timer = window.setInterval(function() {
        /*
        * Run my code...
        */    
    }, 250);
    isIntervalSet = true;
}).mousestop(function() {
    isIntervalSet = false;
    window.clearTimeout(timer);
    timer = null;
});

+1 - 谢谢你的建议!不过,这是最好/最干净的方法吗?感觉有点像个技巧... - Topher Fangio
1
确实有点像是一个hack,window.setInterval更适合这种情况。 - Darin Dimitrov
接受!原来,mousestop 函数正是我所需要的,将完全消除超时代码。非常感谢! - Topher Fangio
这种方法似乎有些问题:如果鼠标不停地移动,mousemove事件会不断触发,但鼠标坐标仍然保持不变。 - Ethan

4

一种解决方案和一个问题^^

如果不使用全局变量,这种方法是否是一个合适的解决方案?

$(function() {
    $("#foo").mousemove((function() {
        var timer = null;

        return function() {
            if (timer !== null) {
                window.clearTimeout(timer);
            }
            timer = window.setTimeout(foo, 250);
        };
    })());
});

function foo() {
    //...
}

1
+1 - 我绝对喜欢不使用全局变量。虽然我还没有尝试过这段代码,但它看起来相当干净和直观。 - Topher Fangio

3
在指定时间段内获取鼠标位置的简单方法。
var timer;
var refresh_time = 50;
var x = 0;
jQuery('body').mousemove(function(evt) {
  if (timer)
    clearTimeout(timer);
    timer = setTimeout(function(){
      var mouse_x = evt.clientX;
      if(mouse_x != x){
        x = mouse_x;
        console.log('mouse is on a new x position' + x);    
      }
    }, refresh_time);        
})

2

我知道我来晚了,但这对于访问此线程的人可能有用,以下是我的建议。

使用模运算符和简单的数字增量,您可以最小化性能损失来限制函数的触发速率,如下所示:

var fired = 0;
$('#element').on('mousemove', function(){
   fired++;
   // Fire 5x less than usual
   if(!(fired % 5) || fired == 1) yourFunction();
})

此外,如果您担心达到最大整数限制,可以使用模运算符每X千次命中重置fired变量,或者通过使用mouseout事件来重置。


2
你正在寻找代码的节流/去抖动功能。

http://benalman.com/projects/jquery-throttle-debounce-plugin/ http://drupalmotion.com/article/debounce-and-throttle-visual-explanation

来自 underscore.js 的示例

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

1

这是一个非常有趣的问题。我找到了一种不那么hackish的方法来解决它,你可以查看以下代码片段的实时演示

({
    event: null,
    interval: null,
    init: function(){
        var self = this;
        $(document).bind("mousemove", function(e){self.event=e;});
        this.interval = setInterval(function(){
            /** do what you wish **/
            console.log(self.event);
        }, 250);
        return this;
    },
    stop: function(){
        $(document).unbind("mousemove", this.event);
        clearInterval(this.interval);
    },
}).init();

1

您可以通过使用超时将计时器设置为null来节省几行代码:

var paused = null;

$("body").mousemove(function (e) {
    if (!paused){
        /** your code here **/
        paused = setTimeout(function(){paused=null}, 250);
    }
});

0

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