[收藏]enable marquee in Firefox and Opera/Firefox和Opera下的marquee行为

W3C 上没有marquee标签,在外国大多数优秀的网站(指网页设计的技术上优秀)都不会让东西在自己的页面内飘来飘去或文字滚来滚去,但marquee行为在某些地方还是很有用的,比如旧系统中沿用下来的功能或者你的领导要求用到。虽然Firefox偶尔也能运行marquee,但大多数情况下,如声明 DOCTPYE为Transitional(或更高)的XHTML,Firfox就不认它了,IE还不算做得绝,做得最绝的是Opera,它从骨子里就认为没有marquee!所以目前有IE支持marquee了。

一、为了在FF和Opera上实现类似marquee的行为,先要了解marquee是怎样的一个行为。

    Marquee特有的属性如下:

    BEHAVIOR:设置或获取文本如何在字幕中滚动。

    值:

scroll Default. Marquee scrolls in the direction specified by the direction property. The text scrolls off the end and starts over.

alternate Marquee scroll direction reverses when its content reaches the edge of the container. 

slide Marquee scrolls in the direction specified by the direction property. The text scrolls to the end and stops.

DIRECTION:设置或获取文本滚动的方向。

值:

left Default. Marquee scrolls left.

right Marquee scrolls right.

down Marquee scrolls down.

up Marquee scrolls up.

SCROLLAMOUNT:设置或获取介于每个字幕绘制序列之间的文本滚动像素数。

值:一个整数。Integer that specifies or receives the number of pixels.

SCROLLDELAY设置或获取字幕滚动的速度,以微秒计算。此值越小,动作就细腻,相当于电影的帖数。

TRUESPEED设置或获取字幕的位置是否使用 scrollDelay scrollAmount 属性计算,已过的实际时间来自于时钟计时。

Boolean:默认为false,简单的说,此时scrollDelay的值总是高于60微秒,低于60微秒的也会归于60微秒。如果想得到scrollDelay小于60微秒的行为,就必须设置TRUESPEEDtrue

false Default. Marquee computes movement based on 60-millisecond ticks of the clock. This means every scrollDelay value under 60 is ignored, and the marquee advances the amount of scrollAmount each 60 milliseconds. For example, if scrollDelay is 6 and scrollAmount is 10, the marquee advances 10 pixels every 60 milliseconds. 

true Marquee advances the pixel value of scrollAmount by the number of milliseconds set for scrollDelay. For example, the marquee would advance 10 pixels for every 6 milliseconds if scrollDelay is 6, scrollAmount is 10, and the value of trueSpeed is true. 

 

二、滚动行为的实现以及对scriptaculousEffect的简单解释

       滚动当然少不了topleft属性,可以在元素的style.topstyle.left中设置。在这里我第一时间想到的就是scriptaculousEffect.Move方法。可惜这个Effect.Move在这里并不适用。因为scriptaculousEffect都有一个事件驱动Effect.Base,这个驱动就像一个发动机,可以为MoveHightLight等行为提供强而有为的能源,能让这些从行为开始的结束提供足够的血液。当然你也可以使用它为自己写的行为特供能量,只要你的行为继承了这个驱动。

那么这个驱动是怎样的一个物件呢?能量又是什么呢?上一段说到从开始的结束,没错,每一个事件都有一个开始和结束,这就是此驱动的特性,必须一个开始和结束,而能量就是一个从01的浮点值,通过驱动的不断递归执行一系列句柄给行为传送“能量”,而句柄的行为方法就是根据这个“能量”为执行从始到终的行为,从而最终达到Effect的目的。

例如想把一个元素从left:0pxleft:300px的移动,在你的方法中就可以这样计算:

currectLeft=能量*300

当“能量”为0时,currectLeft就是0,那么当“能量”为1的时候,cruuectLeft就是300了。

而递归执行的句柄就是一个eventevent内执行的就是你写的方法,你可以在过程中写如下方法,而且从下面这方法中你可以看到Effect的整个生命过程:

     event     name            remark

       event:     beforeStart,           最先执行

       event:      beforeSetup,          行为初始化前执行

       event:     afterSetup,             行为初始化后执行

       event:     beforeUpdate,      从“能量”01多次执行,

       event:     afterUpdate,       从“能量”01多次执行

       event:     beforeFinish,          结束前执行

       event:     afterFinish,            结束后执行

       *另外,你可以在上面的方法中加上Internal后缀(如beforeStartInternal)以表示这是内部执行的,这也是生命周期同时执行的另一条路线。

只要你写的行为包含上面方法,然后继承自Effect.Base就可以实现你想要的了。当然事实上并不是如此的简单,有兴趣可以看看Effect的源码,我所说的只是一个思路。

因为Effect.Base能方便的使效果得以贯彻执行,但在“能量”从01的过程是难以让生命停下来的,而marquee需要的是一条没有终点的时间轴,滚动的目标有终点,但时间不能有终点,而且行为的过程与时间息息相关:每SCROLLDELAY微秒滚动多少像素。所以我仿照Effect.Base写了一个MoveSpeed方法:

var GDnew=function(){}//1

GDnews.Effect=function(){};//2

//上面两个方法是为了扩大命名空间

GDnews.Effect.MoveSpeed=Class.create();

GDnews.Effect.MoveSpeed.prototype={

       initialize:function(element) {

              this.element = $(element);

              this.options = Object.extend({

                     x:    0,

                     y:    0,

                     mode: 'relative',

                     delay:85,

                     amountX:0,

                     amountY:0

                     }, arguments[1] || {});

              this.event("beforeSetup");     

              this.options.amountX=Math.abs(this.options.amountX);

              this.options.amountY=Math.abs(this.options.amountY);

              this.scroX=0;//目前已经移动的X数目

              this.scroY=0;//目前已经移动的Y数目

              // Bug in Opera: Opera returns the "real" position of a static element or

              // relative element that does not have top/left explicitly set.

              // ==> Always set top and left for position relative elements in your stylesheets

              // (to 0 if you do not need them)

              this.element.makePositioned();

              this.originalLeft = parseFloat(this.element.getStyle('left') || '0');

              this.originalTop = parseFloat(this.element.getStyle('top') || '0');

              if(this.options.mode == 'absolute') {

                     // absolute movement, so we need to calc deltaX and deltaY

                     this.options.x = this.options.x - this.originalLeft;

                     this.options.y = this.options.y - this.originalTop;

              }

       this.event("afterSetup");

       this.scroll();   

       },

       update: function() {

              this.event("beforeUpdate");   

              var _x=Math.abs(this.options.x);

              var _y=Math.abs(this.options.y)

              var amountX=(this.scroX+this.options.amountX)>=_x ?

                                   (_x-this.scroX/*归零*/) : this.options.amountX;

              var amountY=(this.scroY+this.options.amountX)>=_y ?

                                   (_y-this.scroY/*归零*/) : this.options.amountY;

              this.scroX+=amountX;//执行X总数累加

              this.scroY+=amountY;//执行Y总数累加

              this.element.setStyle({

                     left:Math.round(this.originalLeft+(this.options.x<0 ? -this.scroX : this.scroX)) + 'px',

                     top:Math.round(this.originalTop+(this.options.y<0 ? -this.scroY : this.scroY)) + 'px'

                     });

              this.event("beforeFinish");

              if (amountX==0 && amountY==0){//结束

                     this.scroY=0;

                     this.scroX=0;

                     this.cancel();

                     this.event("afterFinish");

              }

       },

       scroll:function(){

              this.cancel();

              this.setTimeInt=window.setInterval(this.update.bind(this),this.options.delay);

       },

       cancel:function(){

              if(this.setTimeInt) window.clearInterval(this.setTimeInt);

              this.setTimeInt=null;

       },

       event: function(eventName) {

              if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);

              if(this.options[eventName]) this.options[eventName](this);

       }

}

 

在这个类的基础上,再写一个实现marquee的类:

GDnews.w3cMarquee=Class.create();

/**

*@param element 目标的marquee标签

*@ nomouseEvent 默认为false,类似传统marquee上的onMouseover=this.stop()系列行为

*此方法在IE中不执行。思路是把marquee删除,用一个div代替它,然后设置此divlefttop来实现滚动。

*/

 

GDnews.w3cMarquee.prototype={//目前仅实现behavior=scroll

       initialize:function(element,nomouseEvent){

              if(/MSIE/.test(navigator.userAgent)){//如果是IE,留着真marquee

                     if(nomouseEvent==true) return;

                     element=$(element);

                     Event.observe(element,'mouseover',function(){element.stop()});

                     Event.observe(element,'mouseout',function(){element.start()});

                     return;

              }

              this.marquee=$(element);

              if("marquee"!=this.marquee.tagName.toLowerCase()) return;

             

              this._createDiv();//创建DIV

              this.makePosition();//设置位置

             

              var _x=this.scrollWidth;

              var _y=this.scrollHeight;

              var newElementId=this.newElementId;

              var options={x:_x,y:_y,delay:this.scrollDelay,

                     amountX:this.scrollAmount,amountY:this.scrollAmount,

                     beforeSetup:function(effect){

                            if(nomouseEvent==true) return;

                            var cancel=effect.cancel.bind(effect);

                            var scroll=effect.scroll.bind(effect);

                            Event.stopObserving(newElementId,'mouseover',cancel);

                            Event.stopObserving(newElementId,'mouseout',scroll);

                            Event.observe(newElementId,'mouseover',cancel);

                            Event.observe(newElementId,'mouseout',scroll);

                     },

                     beforeUpdate:function(effect){

//                          if(behavior=='alternate'){//本想实现'alternate'行为,但由于时间关系,我就把它先留着。

//                                 var left=parseInt(effect.element.getStyle('left'));

//                                 var top=parseInt(effect.element.getStyle('top'));

//                                 see(left+","+top);

//                                 if(effect.scroX>=_parentWidth || effect.scroY>_parentHeight){

//                                        effect.options.x=-1*effect.options.x;

//                                        effect.options.y=-1*effect.options.y;

//                                        effect.cancel();

//                                        window.setTimeout(effect.scroll.bind(effect),100);

//                                 }

//                          }

                     },

                     afterFinish:function(effect){

                            /*if(behavior=='scroll'){*/

                                   effect.element.setStyle({left:effect.originalLeft,top:effect.originalTop});

                                   window.setTimeout(effect.scroll.bind(effect),100);

                            /*}*/

                     }

              };

              new GDnews.Effect.MoveSpeed(this.scrollId,options);

       },

       _createDiv:function(){

              this.newElementId=(this.marquee.id || "_marquee")+"_"+(new Date().getTime());

              var _attribute=$A(this.marquee.attributes);

              this.divEl=document.createElement('div');

              this.divEl.id=this.newElementId;

             

              this.scrollId=(this.marquee.id || "_marquee")+"_child_"+(new Date().getTime());

              this.scrollEl=document.createElement('div');

              this.scrollEl.id=this.scrollId;

              try{

                     this.marquee.parentNode.insertBefore(this.divEl,this.marquee);//新建一个node

                     this.divEl.appendChild(this.scrollEl);//一个用于滚动的node

                    

                     _attribute.each(function(d,i){//把其它属性放回去

                                   var key=d.nodeName.toLowerCase();

                                   var nodeValue=d.nodeValue;

                                  

                                   if(typeof nodeValue=='object' || typeof nodeValue=='function'

                                          || nodeValue=='' || nodeValue==null || key=='truespeed' || key=='id') return;

 

                                   switch (key){

                                          case 'direction':

                                                 this.direction=nodeValue;

                                                 return;

                                                 break;

                                          case 'scrolldelay':

                                                 this.scrollDelay=parseInt(nodeValue);

                                                 return;

                                                 break;

                                          case 'scrollamount':

                                                 this.scrollAmount=parseInt(nodeValue);

                                                 return;

                                                 break;

                                          case 'behavior':

                                                 this.behavior=nodeValue;

                                                 return;

                                                 break;

                                          case 'loop':

                                                 this.loop=parseInt(nodeValue);

                                                 return;

                                                 break;

                                   }

                                                                            

                                   this.divEl.setAttribute(key,nodeValue);//IE中只有className而没有class

                            }.bind(this));

                    

                     $A(this.marquee.childNodes).each(function(d){//把子元素放回去

                                                               if(d.length!=1/*这是个长为1textNode*/) this.scrollEl.appendChild(d);

                                                        }.bind(this));

                     this.selfWidth=parseInt($(this.scrollEl).offsetWidth);

                     this.selfHeight=parseInt($(this.scrollEl).offsetHeight);

                    

              }catch(e){

                     throw e;

              }

              Element.remove(this.marquee);//结束真marquee的使命

       },

       makePosition:function(){

              $(this.newElementId).makeClipping();

              var scrollEl=$(this.scrollEl);

              scrollEl.makePositioned();

             

              this.originalLeft = parseFloat(this.scrollEl.getStyle('left') || '0');

              this.originalTop = parseFloat(this.scrollEl.getStyle('top') || '0');

             

             

              this.loop = this.loop || -1;

              this.scrollDelay = this.scrollDelay || 85;

              this.scrollAmount = this.scrollAmount || 6;

              this.direction    = this.direction || 'left';

              this.behavior     = this.behavior || 'scroll';

              if(window.opera) {//fix operaOperamarquee标签有不同的解释

                     switch (this.direction){

                            case '128':

                                   this.direction='left';

                                   break;

                            case '129':

                                   this.direction='right';

                                   break;

                            case '164':

                                   this.direction='up';

                                   break;

                            case '165':

                                   this.direction='down';

                                   break;

                     }

                     if(this.behavior='137') this.behavior='scroll';

                     if(this.behavior='138') this.behavior='alternate';

                     else this.behavior='slide';

              }

              this.behavior     = this.behavior || 'scroll';

              this.parentWidth = parseInt(this.divEl.offsetWidth);

              this.parentHeight = parseInt(this.divEl.offsetHeight)

              this.scrollWidth = this.parentWidth+this.selfWidth;

              this.scrollHeight = this.parentHeight+this.selfHeight;

             

              var topLeft={};

              switch (this.direction){

                     case 'up':

                            this.scrollWidth=0;

                            this.scrollHeight=-this.scrollHeight;

                            topLeft={top:this.parentHeight+"px",left:this.originalLeft+"px"};

                            break;

                     case 'down':

                            this.scrollWidth=0;

                            topLeft={top:(-this.selfHeight-this.originalTop)+"px",left:this.originalLeft+"px"};

                            break;

                     case 'right':

                            this.scrollHeight=0;

                            topLeft={top:this.originalTop+"px" ,left:-this.selfWidth+"px"};

                            break;

                     default:

                            this.scrollHeight=0;

                            this.scrollWidth=-this.scrollWidth;

                            topLeft={top:this.originalTop+"px",left:this.parentWidth+"px"}; 

              }

              scrollEl.setStyle(topLeft);

              scrollEl.show();

       }

}

 

GDnews.w3cMarquee.initAll=function(){

       var marquees=$A(document.getElementsByTagName('marquee'));

       marquees.each(function(d){new GDnews.w3cMarquee(d);});

}

 

代码解释:因为在完成上面代码后,发现在Opera下可以轻松的运行,而在Firefox下就占六成以上CPU了,所以我就怕了,先停一停,发上来和大家讨论一下,或者有更好的方法。

如果想应用以上代码,只要在marquee之后执行一下GDnews.w3cMarquee.initAll()就可以了。

posted @ 2007-02-06 11:01  PointNet  阅读(2273)  评论(0编辑  收藏  举报