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());