How to throttle function call on mouse event using D3.js

I call the function in the "mousemove" event of the DOM element using D3.js .on(), for example:

d3.select("#myelement").on("mousemove", myfunc);
function myfunc(){
    // Just to show that I need to get the mouse coordinates here
    console.log(d3.mouse(this));
}

I need a function that I call to know about the event, i.e. mouse coordinates.

Since the rest of my code is pretty expensive to compute, I would like to disconnect calls until myfunc, say, every 200 ms.

How can I do this by storing the value thisin myfunc(so it d3.mouse(this)still works)? I tried this debounce function: https://davidwalsh.name/javascript-debounce-function And also this: https://remysharp.com/2010/07/21/throttling-function-calls But I can not get them to work like this as I want.

+4
source share
4 answers

Thanks to Gerardo Furtado's answer, I managed to solve my problem by adapting the throttle function from this page like this:

function throttle(fn, threshhold, scope) {
  threshhold || (threshhold = 250);
  var last,
      deferTimer;
  return function () {
    var context = scope || this;

    var now = +new Date,
        args = arguments,
        event = d3.event;
    if (last && now < last + threshhold) {
      // hold on to it
      clearTimeout(deferTimer);
      deferTimer = setTimeout(function () {
        last = now;
          d3.event = event;
        fn.apply(context, args);
      }, threshhold);
    } else {
      last = now;
        d3.event = event;
      fn.apply(context, args);
    }
  };
}
});

Now the callback knows that d3.event and d3.mouse (this) can be used normally inside the function.

+2
source

The problem is not passing thisthe debounce function, which is pretty simple, as you can see in this JSFiddle (I bind JSFiddle because the Stack fragment hangs when registering thisor choosing D3).

D3: d3.event null , . d3.mouse() Cannot read property 'sourceEvent' of null.

, , , D3:

function debounce(fn, delay) {
    var timer = null;
    return function() {
        var context = this,
            args = arguments,
            evt = d3.event;
            //we get the D3 event here
        clearTimeout(timer);
        timer = setTimeout(function() {
            d3.event = evt;
            //and use the reference here
            fn.apply(context, args);
        }, delay);
    };
}

, , :

var circle = d3.select("circle");

circle.on("mousemove", debounce(function() {
  console.log(d3.mouse(this));
}, 250));

function debounce(fn, delay) {
  var timer = null;
  return function() {
    var context = this,
      args = arguments,
      evt = d3.event;
    clearTimeout(timer);
    timer = setTimeout(function() {
    	d3.event = evt;
      fn.apply(context, args);
    }, delay);
  };
}
.as-console-wrapper { max-height: 30% !important;}
<script src="https://d3js.org/d3.v4.js"></script>
<svg>
  <circle cx="120" cy="80" r="50" fill="teal"></circle>
</svg>
Hide result

PS: JSFiddle, Stack , , , mousemove. .

+4

: , this , .

, throttle debounce, , ​​ lodash, API :

jQuery, d3:

// Avoid excessively updating the position while scrolling.
jQuery(window).on('scroll', _.throttle(updatePosition, 100));

https://lodash.com/docs/#throttle

0

, D3js, . , , . , . , "" , .

  • .
  • , MS.
  • MS.
  • , .
  • , , "".
  • .
  • .

, .

var cachedThrottleFuncs = [],
    minimumInterval = 200; // minimum interval between throttled function calls
function throttle(func, obj, evt) {
    var timeouttype = 0,
        curFunc;
    function lowerTimeoutType(f){
        timeouttype=0;
        if (curFunc !== undefined){
            curFunc();
            curFunc = undefined;
        }
    };
    return cachedThrottleFuncs[ ~(
        ~cachedThrottleFuncs.indexOf(func) || 
        ~(
          cachedThrottleFuncs.push(function(Evt) {
            switch (timeouttype){
                case 0: // Execute immediatly
                    ++timeouttype;
                    func.call(Evt.target, Evt);
                    setTimeout(lowerTimeoutType, minimumInterval);
                    break;
                case 1: // Delayed execute
                    curFunc = func.bind(Evt.target, Evt);
                    Evt.preventDefault();
            }
          }) - 1
        )
    )];
};
function listen(obj, evt, func){
    obj.addEventListener(evt, throttle(func, obj, evt));
};
function mute(obj, evt, func){
    obj.removeEventListener(evt, throttle(func, obj, evt));
}

:

listen(document.body, 'scroll', function whenbodyscrolls(){
    if (document.body.scrollTop > 400)
        mute(document.body, 'scroll', whenbodyscrolls();
    else
        console.log('Body scrolled!')
});

, , , .

var minimumInterval = 200; // minimum interval between throttled function calls
function throttle(func, obj, evt) {
    var timeouttype = 0,
        curEvt = null;
    function lowerTimeoutType(f){
        timeouttype=0;
        if (curEvt !== null){
            func(curEvt);
            curEvt = null;
        }
    };
    return function(Evt) {
        switch (timeouttype){
            case 0: // Execute immediately
                ++timeouttype; // increase the timeouttype
                func(Evt);
                // Now, make it so that the timeouttype resets later
                setTimeout(lowerTimeoutType, minimumInterval);
                break;
            case 1: // Delayed execute
                // make it so that when timeouttype expires, your function
                // is called with the freshest event
                curEvt = Evt;
                Evt.preventDefault();
        }
    };
};

200 . , minimumInterval.

0

Source: https://habr.com/ru/post/1674812/


All Articles