原文链接:http://blog.csdn.net/lovelyelfpop/article/details/72730455
一、前言
以下场景往往由于事件频繁被触发,因而频繁执行DOM操作、资源加载等重行为,导致UI停顿甚至浏览器崩溃。
window对象的
resize
、scroll
事件拖拽时的
mousemove
事件射击游戏中的
mousedown
、keydown
事件文字输入、自动完成的
keyup
事件
实际上对于window的resize
事件,实际需求大多为停止改变大小n毫秒后执行后续处理;而其他事件大多的需求是以一定的频率执行后续处理。针对这两种需求就出现了debounce和throttle两种解决办法。
函数去抖(debounce)
如果用手指一直按住一个弹簧,它将不会弹起直到你松手为止。
也就是说当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。
函数节流(throttle)
如果将水龙头拧紧直到水是以水滴的形式流出,那你会发现每隔一段时间,就会有一滴水流出。
也就是会说预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期。
二、Sencha Touch/ExtJS 中防止频繁触发点击事件
比如我们想要这样的效果:
用户在500ms
内,如果多次点击按钮或列表项,那么点击事件只触发一次(即第一次点击时触发)。
我们可以拦截点击事件的触发(fireEvent
),这就需要用到sencha中的before
和after
来实现”拦截器”了。
下面代码有两个实现,一种用Mixin
实现,另一种用Plugin
实现。其中前者用在类中(即define
的时候),后者用在类的实例(Ext.Create
/Ext.widget
/Ext.factory
等)中。
支持Ext.Button
和Ext.DataView
、Ext.List
。
支持自定义节流的时间间隔,单位ms
,默认500ms
。
Mixin:
//ExtJS 6
Ext.define("UX.mixin.Throttle", {
extend: "Ext.Mixin", //如果是Sencha Touch, 这里换成"Ext.mixin.Mixin"
mixinConfig: {
id: 'throttle',
before: {
onTap: 'onBeforeTapThrottle',
onItemTap: 'onBeforeItemTapThrottle'
},
after: {
onTap: 'onAfterTapThrottle',
onItemTap: 'onAfterItemTapThrottle'
}
},
throttleDuration: 500,
onBeforeTapThrottle: function() {
//console.log('before tap');
if (this._justTapped && new Date().getTime() - this._justTapped <= this.throttleDuration) {
//console.log('throttle');
return false;
}
return true;
},
onAfterTapThrottle: function() {
//console.log('after tap');
this._justTapped = new Date().getTime();
},
onBeforeItemTapThrottle: function() {
//console.log('before itemtap');
if (this._itemJustTapped && new Date().getTime() - this._itemJustTapped <= this.throttleDuration) {
//console.log('throttle');
return false;
}
return true;
},
onAfterItemTapThrottle: function() {
//console.log('after itemtap');
this._itemJustTapped = new Date().getTime();
}
});
//Sencha Touch
Ext.define("UX.mixin.Throttle", {
extend: "Ext.mixin.Mixin",
mixinConfig: {
id: 'throttle',
beforeHooks: {
onBeforeTapThrottle: 'onTap',
onBeforeItemTapThrottle: 'onItemTap'
},
afterHooks: {
onAfterTapThrottle: 'onTap',
onAfterItemTapThrottle: 'onItemTap'
}
},
throttleDuration: 500,
onBeforeTapThrottle: function() {
//console.log('before tap');
if (this._justTapped && new Date().getTime() - this._justTapped <= this.throttleDuration) {
//console.log('throttle');
return false;
}
return true;
},
onAfterTapThrottle: function() {
//console.log('after tap');
this._justTapped = new Date().getTime();
},
onBeforeItemTapThrottle: function() {
//console.log('before itemtap');
if (this._itemJustTapped && new Date().getTime() - this._itemJustTapped <= this.throttleDuration) {
console.log('throttle');
return false;
}
return true;
},
onAfterItemTapThrottle: function() {
//console.log('after itemtap');
this._itemJustTapped = new Date().getTime();
}
});
用法如下:
//requires: 'UX.mixin.Throttle'
//自定义list类
Ext.define('MyApp.view.MyList', {
extend: 'Ext.List',
mixins: ['UX.mixin.Throttle'],
throttleDuration: 1000 //节流的时间间隔, 单位ms, 默认500ms
});
//自定义button类
Ext.define('MyApp.view.MyButton', {
extend: 'Ext.Button',
mixins: ['UX.mixin.Throttle'],
throttleDuration: 1000 //节流的时间间隔, 单位ms, 默认500ms
});
Plugin:
Ext.define('UX.plugin.Throttle', {
extend: 'UX.mixin.Throttle',
alias: 'plugin.throttle',
pluginId: 'throttle',
isPlugin: true,
init: function(cmp) {
if (cmp instanceof Ext.DataView) {
cmp.onBefore({
itemtap: 'onBeforeItemTapThrottle',
scope: this
});
cmp.onAfter({
itemtap: 'onAfterItemTapThrottle',
scope: this
});
} else {
cmp.onBefore({
tap: 'onBeforeTapThrottle',
scope: this
});
cmp.onAfter({
tap: 'onAfterTapThrottle',
scope: this
});
}
}
});
用法如下:
//requires: 'UX.plugin.Throttle'
{
xtype: 'button',
text: '扫一扫',
handler: function() {
//点击事件的逻辑
},
plugins: ['throttle']
}, {
xtype: 'button',
text: '完成',
handler: function() {
//点击事件的逻辑
},
plugins: [{
type: 'throttle',
throttleDuration: 1000 //节流的时间间隔, 单位ms, 默认500ms
}]
}, {
xtype: 'list',
plugins: ['throttle']
}
测试结果
连续快速2次点击,控制台输出如下:
dataview
/list
测试结果
before itemtap
itemtap
after itemtap
before itemtap
throttle
after itemtap
button
测试结果
before tap
tap
after tap
before tap
throttle
after tap