ElementW 2011-09-06
类Ext.LoadMask
这一节分析Ext中遮罩效果的实现,Ext中专门为遮罩效果提供了类Ext.LoadMask,该类封装了Ext.Element中实现遮罩的方法mask和unmask(这两个方法的定义在源文件Element.fx-more.js中)。该类用于在加载数据时为元素做出类似于遮罩的效果。如果配置了Ext.data.Store,则可将效果与Ext.data.Store的加载达到同步,并且mask本身也会被缓存起来从而可以为其他加载复用。对于所有的元素(Element),这个遮罩类会替换元素本身的Updater加载指示器,并且在初始化完毕后销毁。
先看Element.fx-more.js中的方法mask和unmask
/**
* 在元素上加入一个遮罩来禁止用户操作,该效果依赖于core.css
* 这个方法只能用于可接受子节点的元素上
* Puts a mask over this element to disable user interaction. Requires core.css.
* This method can only be applied to elements which accept child nodes.
* @param {String} msg (optional) A message to display in the mask 遮罩时的提示信息
* @param {String} msgCls (optional) A css class to apply to the msg element 遮罩元素的css样式
* @return {Element} The mask element 返回遮罩蒙板元素
*/
mask : function(msg, msgCls) {
var me = this,
dom = me.dom,
dh = Ext.DomHelper,
EXTELMASKMSG = "ext-el-mask-msg",
el,
mask;
//如果是非body,并且该元素的样式position为static时,需要把应用遮罩效果的元素设为relative
//这里需要说一下position,该样式属性可以把元素放置到一个静态的、相对的、绝对的、或固定的位置中
/**
* 值 描述
static 默认。位置设置为 static 的元素,它始终会处于页面流给予的位置(static 元素会忽略任何 top、bottom、left 或 right 声明)。
relative 位置被设置为 relative 的元素,可将其移至相对于其正常位置的地方,因此 "left:20" 会将元素移至元素正常位置左边 20 个像素的位置。
absolute 位置设置为 absolute 的元素,可定位于相对于包含它的元素的指定坐标。此元素的位置可通过 "left"、"top"、"right"
以及 "bottom" 属性来规定。
fixed 位置被设置为 fixed 的元素,可定位于相对于浏览器窗口的指定坐标。此元素的位置可通过 "left"、"top"、"right"
以及"bottom" 属性来规定。不论窗口滚动与否,元素都会留在那个位置。工作于 IE7(strict 模式)。
*/
if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
me.addClass(XMASKEDRELATIVE);
}
//先删除element maskMsg 和 mask,从Element缓存中取得
if (el = data(dom, 'maskMsg')) {
el.remove();
}
if (el = data(dom, 'mask')) {
el.remove();
}
//添加新的遮罩层,样式ext-el-mask默认透明度为0.5,z-index为100,这样如果被遮罩的元素z-index高于100,就不能起到遮罩的效果了
//要想达到该效果,只需重新设置z-index即可,mask.setStyle('z-index',1000);
mask = dh.append(dom, {cls : "ext-el-mask"}, true);
data(dom, 'mask', mask);
me.addClass(XMASKED);
mask.setDisplayed(true);
//添加提示信息
if (typeof msg == 'string') {
var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
data(dom, 'maskMsg', mm);
mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
mm.dom.firstChild.innerHTML = msg;
mm.setDisplayed(true);
mm.center(me);
}
// ie will not expand full height automatically
//对于ie需重新设置其高度
if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
mask.setSize(undefined, me.getHeight());
}
return mask;
},
/**
* 移除之前用过的遮罩
* Removes a previously applied mask.
*/
unmask : function() {
var me = this,
dom = me.dom,
mask = data(dom, 'mask'),
maskMsg = data(dom, 'maskMsg');
if (mask) {
if (maskMsg) {
maskMsg.remove();
data(dom, 'maskMsg', undefined);
}
mask.remove();
data(dom, 'mask', undefined);
me.removeClass([XMASKED, XMASKEDRELATIVE]);
}
},利用这两个方法可以很方便的给元素加载遮罩效果,如果被遮罩的元素z-index大于100,则只需重新设置遮罩元素的z-index即可,代码中已说明过mask.setStyle('z-index',1000); 另外方法isMasked可以判断Element是否应用了遮罩效果
/**
* 判断该Element是否应用了遮罩效果
* Returns true if this element is masked
* @return {Boolean}
*/
isMasked : function() {
var m = data(this.dom, 'mask');
return m && m.isVisible();
},类Ext.LoadMask
该类封装了上面提到的方法mask和unmask,如果实例化类Ext.LoadMask时,传入的参数中有store对象,则会自动在加载store时处理遮罩层效果,并且加载完后销毁遮罩层,如果不传入则需手动调用show、hide来显示和隐藏遮罩层,代码如下
Ext.LoadMask = function(el, config){
this.el = Ext.get(el);
Ext.apply(this, config);
if(this.store){
this.store.on({
scope: this,
beforeload: this.onBeforeLoad,
load: this.onLoad,
exception: this.onLoad
});
this.removeMask = Ext.value(this.removeMask, false);
}else{
var um = this.el.getUpdater();//获取这个元素的UpdateManager。
um.showLoadIndicator = false; // disable the default indicator不显示默认的指示器,即提示信息
um.on({
scope: this,
beforeupdate: this.onBeforeLoad,
update: this.onLoad,
failure: this.onLoad
});
this.removeMask = Ext.value(this.removeMask, true);
}
};方法show调用了onBeforeLoad
onBeforeLoad : function(){
if(!this.disabled){
this.el.mask(this.msg, this.msgCls);
}
},该方法调用了Element中的方法mask来显示遮罩,而方法hide调用了onLoad
onLoad : function(){
this.el.unmask(this.removeMask);
},该方法调用了Element中的方法unmask来销毁遮罩层
用法,在Ext组件GridPanel中使用了Ext.LoadMask来实现遮罩效果,代码如下
if(this.loadMask){//显示遮罩层
this.loadMask = new Ext.LoadMask(this.bwrap,
Ext.apply({store:this.store}, this.loadMask));
}GridPanel中遮罩层显示在this.bwrap上,在使用过程中也可以参考GridPanel的使用
Ext.LoadMask有个缺点就是不能动态的设置遮罩层的z-index,如果被遮罩的组件或元素z-index设置的太大,则利用该类实现遮罩时,遮罩效果失败。解决问题有两种方法,一种就是直接利用Element.mask和Element.unMask来显示、销毁遮罩,同时设置z-index,mask.setStyle('z-index',1000);这样对于store就不能达到实际的效果,还需动态的书写store加载前、加载后和加载失败的遮罩处理代码,另一种方法就是直接修改或扩展Ext.LoadMask的方法onBeforeLoad来返回遮罩对象(即把this.mask暴露出来,用户可调用),代码如下
onBeforeLoad : function(){
if(!this.disabled){
this.mask = this.el.mask(this.msg, this.msgCls);
}
},其实最简单的方法就是,通过以下代码来获取mask对象并且设置z-index
var mask = el.query ('div. ext-el-mask')[0];
//或
//var mask = el. child ('div. ext-el-mask');
mask.setStyle('z-index',1000);参考例子
var mask = new Ext.LoadMask(Ext.getBody(), {
removeMask: true,
msg: '正在加载,请稍等...'
});
mask.show();
//延迟执行
var task = new Ext.util.DelayedTask(mask.hide, mask);
task.delay(5000);//5秒后执行自定义扩展遮罩层
除了Ext提供的遮罩层封装类,用户也可以自己扩展定义,如下
this.mask = Ext.DomHelper.append(Ext.getBody(),{cls:'ext-el-mask'},true);
this.mask.enableDisplayMode('block');
this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
this.mask.setStyle('z-index', 100000);首先创建遮罩层元素,并设置其隐藏方式,然后设置遮罩层高度和宽度,最后修改z-index,在不需要显示遮罩时隐藏或销毁遮罩层,代码如下
this.mask.hide(); Ext.destroy( this.mask );
如果还想加上遮罩提示信息,则可以加入如下代码
var maskMsg = Ext.DomHelper.append(Ext.getBody(), {
cls: 'ext-el-mask-msg',
cn: {
tag: 'div',
cls: 'x-mask-loading',
html: '正在加载,请稍等...'
}
}, true);
maskMsg.setDisplayed(true);
maskMsg.center(Ext.getBody());