幻灯片事件的延迟回调函数

时间:2016-05-31 03:16:07

标签: jquery jquery-ui timeout delay jquery-ui-slider

我正在使用jqueryui的滑块小部件 我希望在滑块的滑动事件上运行一个函数,但我只希望它在(700ms)延迟后运行。如果滑块手柄继续移动(即再次触发滑动),那么我希望延迟超时重置。

我试图使用jquery的doTimeout以及setTimeout无济于事。我做的任何事情似乎都会阻止滑块手柄改变位置。

var heavy = {
    updatesomething: function() {
        //some computationally heavy code
    }
}

$('#slider').slider({
    min:1, max:99, orientation: 'vertical',
    slide: function(event, ui) {
        $(this).doTimeout('timeoutid', 700, function() {
            heavy.updatesomething(this, ui.value);  });
    }
});

2 个答案:

答案 0 :(得分:0)

使用可用的去抖动实现。以下示例来自davidwalsh blog。另外,不是传递上下文,而是使用apply,如下所示,使用正确的上下文和参数调用函数。

var heavy = {
  updatesomething: function(event, ui) {
    console.log(this);
    console.log(ui);
  }
}

// taken from https://davidwalsh.name/javascript-debounce-function
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);
  };
};

$('#slider').slider({
  min: 1,
  max: 99,
  orientation: 'vertical',
  slide: debounce(function(event, ui) {
    heavy.updatesomething.apply(this, arguments);
    //---------------------------^-context  ^-- what it says
  }, 700)
});
<link href="//code.jquery.com/ui/1.11.4/themes/ui-lightness/jquery-ui.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<div id="slider"></div>

答案 1 :(得分:0)

在这种情况下,去抖动的问题在于,如果用户继续移动滑块,则不会有任何事件。因此,使用限制更有用。

以下示例还包括完整的lodash Throttle和Debounce

'use strict';

/**
 * Creates a debounced function that delays invoking `func` until after `wait`
 * milliseconds have elapsed since the last time the debounced function was
 * invoked. The debounced function comes with a `cancel` method to cancel
 * delayed `func` invocations and a `flush` method to immediately invoke them.
 * Provide `options` to indicate whether `func` should be invoked on the
 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
 * with the last arguments provided to the debounced function. Subsequent
 * calls to the debounced function return the result of the last `func`
 * invocation.
 *
 * **Note:** If `leading` and `trailing` options are `true`, `func` is
 * invoked on the trailing edge of the timeout only if the debounced function
 * is invoked more than once during the `wait` timeout.
 *
 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
 *
 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
 * for details over the differences between `_.debounce` and `_.throttle`.
 *
 * from lodash - https://github.com/lodash/lodash/blob/4.16.2/lodash.js#L10218
 */
function debounce(func, wait, options) {
	if (!options) options = {};

	let lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0,
		leading = false,
		maxing = false,
		trailing = true;

	wait = wait || 0;
	leading = !!options.leading;
	maxing = 'maxWait' in options;
	maxWait = maxing ? Math.max(options.maxWait || 0, wait) : maxWait;
	trailing = 'trailing' in options ? !!options.trailing : trailing;

	function invokeFunc(time) {
		const args = lastArgs,
			thisArg = lastThis;

		lastArgs = lastThis = undefined;
		lastInvokeTime = time;
		result = func.apply(thisArg, args);
		return result;
	}

	function leadingEdge(time) {
		// Reset any `maxWait` timer.
		lastInvokeTime = time;
		// Start the timer for the trailing edge.
		timerId = setTimeout(timerExpired, wait);
		// Invoke the leading edge.
		return leading ? invokeFunc(time) : result;
	}

	function remainingWait(time) {
		const timeSinceLastCall = time - lastCallTime,
			timeSinceLastInvoke = time - lastInvokeTime,
			result = wait - timeSinceLastCall;

		return maxing ? Math.min(result, maxWait - timeSinceLastInvoke) : result;
	}

	function shouldInvoke(time) {
		const timeSinceLastCall = time - lastCallTime,
			timeSinceLastInvoke = time - lastInvokeTime;

		// Either this is the first call, activity has stopped and we're at the
		// trailing edge, the system time has gone backwards and we're treating
		// it as the trailing edge, or we've hit the `maxWait` limit.
		return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
			(timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
	}

	function timerExpired() {
		const time = Date.now();
		if (shouldInvoke(time)) {
			return trailingEdge(time);
		}
		// Restart the timer.
		timerId = setTimeout(timerExpired, remainingWait(time));
	}

	function trailingEdge(time) {
		timerId = undefined;

		// Only invoke if we have `lastArgs` which means `func` has been
		// debounced at least once.
		if (trailing && lastArgs) {
			return invokeFunc(time);
		}
		lastArgs = lastThis = undefined;
		return result;
	}

	function cancel() {
		if (timerId !== undefined) {
			clearTimeout(timerId);
		}
		lastInvokeTime = 0;
		lastArgs = lastCallTime = lastThis = timerId = undefined;
	}

	function flush() {
		return timerId === undefined ? result : trailingEdge(Date.now());
	}

	function debounced(...args) {
		const time = Date.now(),
			isInvoking = shouldInvoke(time);

		lastArgs = args;
		lastThis = this;
		lastCallTime = time;

		if (isInvoking) {
			if (timerId === undefined) {
				return leadingEdge(lastCallTime);
			}
			if (maxing) {
				// Handle invocations in a tight loop.
				timerId = setTimeout(timerExpired, wait);
				return invokeFunc(lastCallTime);
			}
		}
		if (timerId === undefined) {
			timerId = setTimeout(timerExpired, wait);
		}
		return result;
	}
	debounced.cancel = cancel;
	debounced.flush = flush;
	return debounced;
}

/**
 * Creates a throttled function that only invokes `func` at most once per
 * every `wait` milliseconds. The throttled function comes with a `cancel`
 * method to cancel delayed `func` invocations and a `flush` method to
 * immediately invoke them. Provide `options` to indicate whether `func`
 * should be invoked on the leading and/or trailing edge of the `wait`
 * timeout. The `func` is invoked with the last arguments provided to the
 * throttled function. Subsequent calls to the throttled function return the
 * result of the last `func` invocation.
 *
 * **Note:** If `leading` and `trailing` options are `true`, `func` is
 * invoked on the trailing edge of the timeout only if the throttled function
 * is invoked more than once during the `wait` timeout.
 *
 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
 *
 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
 * for details over the differences between `_.throttle` and `_.debounce`.
 */
function throttle(func, wait, options) {
	if (!options) options = {};
	let leading = true,
		trailing = true;

	leading = 'leading' in options ? !!options.leading : leading;
	trailing = 'trailing' in options ? !!options.trailing : trailing;

	return debounce(func, wait, {
		'leading': leading,
		'maxWait': wait,
		'trailing': trailing
	});
}


let counter=0;

$('#slider').slider({
  min: 1,
  max: 99,
  orientation: 'vertical',
  slide: throttle(function(event, ui) {
    $(event.target).slider('option', 'value', ui.value); // making sure value is correct
    console.log(ui.value);
  }, 100)
});
<link href="//code.jquery.com/ui/1.11.4/themes/ui-lightness/jquery-ui.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<div id="slider"></div>