JS image stabilization function and the throttle

Shake

Image stabilization technology that is more than can be invoked sequentially combined into one, in a certain time to stop the operation of the provisions of events was only triggered once.

Popular point, take a look at the following simplified example:

// 防抖动函数
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);
    };
};
 
var myEfficientFn = debounce(function() {
    // 滚动中的真正的操作
}, 250);
 
// 绑定监听
window.addEventListener('resize', myEfficientFn);

If the two functions is probably no continuous scroll event trigger within 250ms, then will we really want to trigger the trigger function in the scroll event.

Throttling (Throttling)

Anti-shake function really good, but there are problems, such as lazy loading of the picture, I hope that in the process of falling constantly being loaded out of the picture, and not only when I stopped falling when the picture was only load it. Or the decline of the ajax request when the data load is the same reason.

This time, we hope that even if the page is scrolled continuously, but the scroll handler can also be triggered at a certain frequency (for example, trigger a 250ms), this type of scenario, it is necessary to use another technique, known as throttling function (throttling ).

Throttling function, a function is executed only once in the X milliseconds.

Compared with image stabilization, the throttle function main difference is that it guarantees the implementation of at least one event handler we want to trigger in X milliseconds.

Compared with image stabilization, a multi-function expansion mustRun attribute representative of the mustRun milliseconds, inevitably trigger a Handler, using the same timer to see simple example

// 简单的节流函数
function throttle (func, wait, mustRun) {
    var timeout,
        startTime = new Date();
 
    return function () {
        var context = this,
            args = arguments,
            curTime = new Date();
 
        clearTimeout(timeout);
        // 如果达到了规定的触发时间间隔,触发 handler
        if(curTime - startTime >= mustRun){
            func.apply(context, args);
            startTime = curTime;
        // 没达到触发间隔,重新设定定时器
        } else {
            timeout = setTimeout(func, wait);
        }
    };
};
// 实际想绑定在 scroll 事件上的 handler
function realFunc(){
    console.log("Success");
}
// 采用了节流函数
window.addEventListener('scroll', throttle(realFunc,500,1000));

The above example a simple function of the throttle can try to get the next browser, probably function is to scroll if some time has been triggered interval shorter than 500ms, then we want to ensure that the event handler is invoked at least once within 1000ms triggers .

Use rAF (requestAnimationFrame) triggering scroll event

window.requestAnimationFrame () This method is used before the page is redrawn, it tells the browser to call a specified function. This method takes a function as a parameter, the function will be called before the redraw.

rAF commonly used in web animation production, for precise control frame refresh the page rendering, allowing more fluid animation, of course, its role is not limited to animation, we can take advantage of its features it as a timer. (Of course, it is not a timer)

Generally speaking, rAF frequency is called 60 times per second, that is 1000/60, the trigger frequency is about 16.7ms. (When performing complex operations, when it is found it impossible to maintain the frequency of 60fps, it will reduce the frequency to maintain stable 30fps frame number.)

var ticking = false; // rAF 触发锁
 
function onScroll(){
    if(!ticking) {
        requestAnimationFrame(realFunc);
        ticking = true;
    }
}
 
function realFunc(){
    // do something...
    console.log("Success");
    ticking = false;
}
// 滚动事件监听
window.addEventListener('scroll', onScroll, false);

Examples of simple use rAF above may try to get the next browser, probably function is in the process of rolling, maintaining a frequency of 16.7ms trigger event handler.

RequestAnimationFrame advantages and disadvantages of using co-exist, first of all we have to consider its compatibility issues, and secondly because it can only be achieved at a frequency of 16.7ms to trigger, on behalf of its adjustability is very poor. But compared to the throttle (func, xx, 16.7), when used for more complex scenarios, rAF could better results and better performance.

to sum up

debounce anti-shake: anti-shake technology that is more than can be invoked sequentially combined into one, that is, within a certain period of time, a predetermined number of events are triggered.

throttling the throttle function: Only one function to perform in the X milliseconds, only when the last function executed after a specified time interval you can for the next call to the function.

requestAnimationFrame: 16.7ms trigger a handler, reduced controllability, but to enhance the performance and accuracy.

reference

http://www.cnblogs.com/coco1s/p/5499469.html

Appendix - other mature library

Providing more mature underscore stabilization throttle function, see below

Anti-shake function

/**
 * 空闲控制 返回函数连续调用时,空闲时间必须大于或等于 wait,func 才会执行
 *
 * @param  {function} func        传入函数
 * @param  {number}   wait        表示时间窗口的间隔
 * @param  {boolean}  immediate   设置为ture时,调用触发于开始边界而不是结束边界
 * @return {function}             返回客户调用函数
 */
_.debounce = function (func, wait, immediate) {
    var timeout, args, context, timestamp, result;

    var later = function () {
        // 据上一次触发时间间隔
        var last = _.now() - timestamp;

        // 上次被包装函数被调用时间间隔last小于设定时间间隔wait
        if (last < wait && last > 0) {
            timeout = setTimeout(later, wait - last);
        } else {
            timeout = null;
            // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
            if (!immediate) {
                result = func.apply(context, args);
                if (!timeout) context = args = null;
            }
        }
    };

    return function () {
        context = this;
        args = arguments;
        timestamp = _.now();
        var callNow = immediate && !timeout;
        // 如果延时不存在,重新设定延时
        if (!timeout) timeout = setTimeout(later, wait);
        if (callNow) {
            result = func.apply(context, args);
            context = args = null;
        }

        return result;
    };
};

Throttle function

/**
 * 频率控制 返回函数连续调用时,func 执行频率限定为 次 / wait
 * 
 * @param  {function}   func      传入函数
 * @param  {number}     wait      表示时间窗口的间隔
 * @param  {object}     options   如果想忽略开始边界上的调用,传入{leading: false}。
 *                                如果想忽略结尾边界上的调用,传入{trailing: false}
 * @return {function}             返回客户调用函数   
 */
_.throttle = function (func, wait, options) {
    var context, args, result;
    var timeout = null;
    // 上次执行时间点
    var previous = 0;
    if (!options) options = {};
    // 延迟执行函数
    var later = function () {
        // 若设定了开始边界不执行选项,上次执行时间始终为0
        previous = options.leading === false ? 0 : _.now();
        timeout = null;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
    };
    return function () {
        var now = _.now();
        // 首次执行时,如果设定了开始边界不执行选项,将上次执行时间设定为当前时间。
        if (!previous && options.leading === false) previous = now;
        // 延迟执行时间间隔
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
        // 延迟时间间隔remaining小于等于0,表示上次执行至此所间隔时间已经超过一个时间窗口
        // remaining大于时间窗口wait,表示客户端系统时间被调整过
        if (remaining <= 0 || remaining > wait) {
            clearTimeout(timeout);
            timeout = null;
            previous = now;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
            //如果延迟执行不存在,且没有设定结尾边界不执行选项
        } else if (!timeout && options.trailing !== false) {
            timeout = setTimeout(later, remaining);
        }
        return result;
    };
};

Guess you like

Origin www.cnblogs.com/everlose/p/12501229.html