簡単・高性能 marquee を超えた滑らかスクロール

【特徴】
 ・本家 marquee よりも動きが滑らか。
 ・本家 marquee は 一部ブラウザで動作がおかしかったり動作しなかったりする(した)がこれは大丈夫。
 ・使い方はスクリプトの知識ゼロで問題なし。本家と同じくらい簡単。

下記のスクリプトをヘッダにコピペしたら、HTML 側では class 名をつけるだけです。
2008年 1月に TAG INDEX javascript 掲示板内で製作しています。
mq で始まる class 属性をつけたタグをスクリプトで検索し自動で発動します。
class に特殊な命名をすることで動き方の詳細な調整も可能です。(後述)

ノーマル動作はこうです。 こんなことも簡単にできます。 これは比較のための本家マーキー。ショボい。

<script type="text/javascript">
<!--
/***************************************************************
 *【概要】
 *  クラス属性が mq〜 で指定されたタグを検索し滑らかにマーキー
 *  ( HTML 側はクラス属性の指定のみでOK)
 *【パラメータ(各々任意指定)】
 *  _d : 移動方向(top, bottom, right, left)指定のない場合 left
 *  _s : スピード( 1 〜 5 )指定のない場合 4 。5 が速い。
 *  _w : 動作範囲の幅を指定( px )
 *  _h : 動作範囲の高さを指定( px )
 *  _z : ??
 *  _m : このスイッチを指定しておくとマウスオーバーでストップする(2009.09追加)
 *【使用例】
 *  <em class="mq_dtop_s2_z">メッセージ</em>
 *【情報】
 *  2008.01  created by 6lOezfNBvA&ai&jax_6@TAG_INDEX
 *
 * ※ 下記の場合には「音楽と環境問題(http://otk.zatunen.com)」から引用したことを明記して下さい。
 *  ・ソース本体を利用する際。
 *  ・ html 側の属性でスクリプトの引数を持つアイデアをこのソースを見て使用する場合。
 *          ☆ あとはご自由にどうぞ ☆
 ***************************************************************/
WINDOWONLOAD();
var itv = new Array();
function WINDOWONLOAD(){
    if(!document.body){
        setTimeout(function(){
            WINDOWONLOAD();
        },100);
    }else{
        setTimeout(function(){
            pickup();
        },50);
    }
}
function pickup(){
    try{
        var dir,speed,tim,ran,wid,hei,z,m;
        var speedTable = new Array();
        var seiki = /(?:(?:^mq)|(?:.*? mq))(.*)/i;
        var seiki_d = /.*(_d(?:top|bottom|right|left)).*/i;
        var seiki_s = /.*(_s[1-5]).*/i;
        var seiki_w = /.*(_w[0-9\.]{1,4}).*/i;
        var seiki_h = /.*(_h[0-9\.]{1,4}).*/i;
        var seiki_z = /.*(_z).*/i; //隠しコマンド(ポコポコする。縦だとウネウネ。)
        var seiki_m = /.*(_m).*/i; //2009.09追加。MouseOverでストップする
        
        // [ tim:刻み時間, ran:一度に動くpx ]
        speedTable[1] = [55, 1];
        speedTable[2] = [35, 1];
        speedTable[3] = [20, 1];
        speedTable[4] = [10, 1];
        speedTable[5] = [10, 2];
    
        // class が mq 〜 で指定してあるタグを先頭から探し、発見次第 mq() を発動
        var mqElements = document.body.getElementsByTagName('*');
        var I = mqElements.length;
        // IE で (!document.body) をすり抜けた場合ここでかかる
        if(mqElements[I-1].tagName=='SCRIPT'){/*msg+="c";*/ WINDOWONLOAD();return;}
        
        for(var i=I-1 ; i>=0 ; i=i-1){
            if(mqclass=mqElements[i].className){
                if(args = mqclass.match(seiki)){
                    //デフォルト値の設定
                    dir = 'left';
                    speed = 4;
                    wid = -1;
                    hei = -1;
                    z = false;
                    m = false;
                    
                    if(arg_d = args[1].match(seiki_d)) dir=arg_d[1].substring(2,arg_d[1].length);
                    
                    if(arg_s = args[1].match(seiki_s)){
                        speed = arg_s[1].substring(2,arg_s[1].length);
                    }
                    tim = speedTable[speed][0];
                    ran = speedTable[speed][1];
                    
                    if(arg_w = args[1].match(seiki_w)) wid=arg_w[1].substring(2,arg_w[1].length)*1;
                    
                    if(arg_h = args[1].match(seiki_h)) hei=arg_h[1].substring(2,arg_h[1].length)*1;
                    
                    if(arg_z = args[1].match(seiki_z)) z=true;
                    
                    if(arg_z = args[1].match(seiki_m)) m=true;
                    
                    mq(mqElements[i],dir,tim,ran,wid,hei,z,m);
                }
            }
        }
    }catch(err){WINDOWONLOAD();}    
}

function zz(inX){  // パラメータ Z 計算用
    var outY = -parseInt((Math.abs((inX*2+10)%40-20)-12)/3.5) + "px";
    return outY;
}

function mq(mqC, d, t, r, w, h, z, m) {
// mqC:文字を流す箇所のnode
//   d:流す方向("top" or "bottom" or "right" or "left")
//   t:刻み時間
//   r: 一度に動く距離
//   w:アニメーションする横幅
//   h:アニメーションする縦幅
//   z: ??(ブーリアン)
//   m: マウスオーバでストップ(ブーリアン)
    var sv_mqC = mqC;
    var mqP;
    var mqCoffsetH;
    var mqCoffsetW;
    var wid;
    var hei;
    //後で親にするためのクローンを作成。画像だったらスタイルをコピーしたDIVを親にする。
    if(mqC.tagName!='IMG'){
        mqP = mqC.cloneNode(false);
    }else{
        mqP = document.createElement('div');
        //スタイル要素で指定されている場合の対策として id と class を移動( img に指定された場合は諦める)
        mqP.id = mqC.id;
        mqP.className = mqC.className;
        mqC.removeAttribute('id');
        mqC.removeAttribute('class');
        mqP.style.cssText = sv_mqC.style.cssText;
        mqC.style.cssText = ""; //子のスタイルは消す
    }
    
    //囲む前にもともとインラインで指定されている可能性を考慮
    mqP.style.display='block';
    
    //作った mqP で外側を囲む(動作範囲)
    try{
        /*@cc_on
        @if(@_jscript)
            mqC.applyElement(mqP,'outside');
        @else*/
                var ran = document.createRange();
            ran.selectNode(mqC);
            ran.surroundContents(mqP);
            mqC = mqP.firstChild; //mqCを再取得
        //@end
    }catch(err){
        try{
            mqP.appendChild(mqC.cloneNode(true));
            mqC.parentNode.replaceChild(mqP, mqC);
            mqC = mqP.firstChild; //mqCを再取得
        }catch(err){
            alert(sv_mqC.tagName + " を動かすのは我慢して下さい。");
        }
    }
    
    //IMG 以外は mqC を div に変えておく。
    if(mqC.tagName!='IMG'){
        mqP.replaceChild(document.createElement('div'),mqC);
        mqC = mqP.firstChild;
        mqCC = sv_mqC.childNodes;
        for(var i=0, I=mqCC.length; i<I; i++){
            mqC.appendChild(mqCC[i].cloneNode(true));
        }
        mqC.width = sv_mqC.width;
        mqC.height = sv_mqC.height;
        /*@if(@_jscript)
            mqC.style.width = sv_mqC.currentStyle.width;
            mqC.style.height = sv_mqC.currentStyle.height;
        @else*/
            mqC.style.width = document.defaultView.getComputedStyle(sv_mqC, null).width;
            mqC.style.height = document.defaultView.getComputedStyle(sv_mqC, null).height;
        //@end
    }

    /*@if(@_jscript)
    if(mqP.currentStyle.position!='absolute'){
    @else*/
    if(document.defaultView.getComputedStyle(mqP, null).position!='absolute'){
    //@end
        mqP.style.position = 'relative';
    }
    
    mqP.style.overflow = 'hidden';

    mqC.style.position = 'absolute';
    mqC.style.whiteSpace = 'nowrap';
    
    mqCoffsetH = mqC.offsetHeight;
    mqCoffsetW = mqC.offsetWidth;

    if(w==-1){wid = mqCoffsetW}else{wid = w;}
    if(h==-1){hei = mqCoffsetH}else{hei = h;}
    mqP.style.width = wid + 'px';
    mqP.style.height = hei + 'px';

/////////確認用仮/////////////    
//mqP.style.borderStyle='solid';
//mqP.style.borderColor='white';
//mqP.style.borderWidth='1px';
/////////仮/////////////    

    var counter = 0;
    var mqArray = new Array();
    var mqArrayZ = new Array();
    var mqArray_length = 0;
    
    switch(d){
        case 'top':
            //まず初期配置に。
            counter= -1 * mqCoffsetH;
            mqC.style.bottom = counter + 'px';
            if(!z){
                //移動先のリストを予め作成しておく。
                for(var i=0 ; counter<hei ; counter+=r, i++){
                    mqArray[i] = counter + 'px';
                }
                counter = 0; //submq のカウンターとして再利用する。
                mqArray_length = mqArray.length; //ループの外で計算しておく。
                var submq = function(){
                    mqC.style.bottom = mqArray[counter++];
                    if(counter >= mqArray_length)counter=0;
                }
            }else{
                //移動先のリストを予め作成しておく。
                for(var i=0 ; counter<hei ; counter+=r, i++){
                    mqArray[i] = counter + 'px';
                    mqArrayZ[i] = zz(i);
                }
                counter = 0;
                mqArray_length = mqArray.length;
                var submq = function(){
                    mqC.style.bottom = mqArray[counter];
                    mqC.style.left = mqArrayZ[counter];
                    counter++;
                    if(counter >= mqArray_length)counter=0;
                }
            }
            break;
        case 'bottom':
            counter= -1 * mqCoffsetH;
            mqC.style.top = counter + 'px';
            if(!z){
                for(var i=0 ; counter<hei ; counter+=r, i++){
                    mqArray[i] = counter + 'px';
                }
                counter = 0;
                mqArray_length = mqArray.length;
                var submq = function(){
                    mqC.style.top = mqArray[counter++];
                    if(counter >= mqArray_length)counter=0;
                }
            }else{
                for(var i=0 ; counter<hei ; counter+=r, i++){
                    mqArray[i] = counter + 'px';
                    mqArrayZ[i] = zz(i);
                }
                counter = 0;
                mqArray_length = mqArray.length;
                var submq = function(){
                    mqC.style.top = mqArray[counter];
                    mqC.style.left = mqArrayZ[counter];
                    counter++;
                    if(counter >= mqArray_length)counter=0;
                }
            }
            break;
        case 'right':
            counter= -1 * mqCoffsetW;
            mqC.style.left = counter + 'px';
            if(!z){
                for(var i=0 ; counter<wid ; counter+=r, i++){
                    mqArray[i] = counter + 'px';
                }
                counter = 0;
                mqArray_length = mqArray.length;
                var submq = function(){
                    mqC.style.left = mqArray[counter++];
                    if(counter >= mqArray_length)counter=0;
                }
            }else{
                for(var i=0 ; counter<wid ; counter+=r, i++){
                    mqArray[i] = counter + 'px';
                    mqArrayZ[i] = zz(i);
                }
                counter = 0;
                mqArray_length = mqArray.length;
                var submq = function(){
                    mqC.style.left = mqArray[counter];
                    mqC.style.top = mqArrayZ[counter];
                    counter++;
                    if(counter >= mqArray_length)counter=0;
                }
            }
            break;
        default: // left
            counter= -1 * mqCoffsetW;
            mqC.style.right = counter + 'px';
            if(!z){
                for(var i=0 ; counter<wid ; counter+=r, i++){
                    mqArray[i] = counter + 'px';
                }
                counter = 0;
                mqArray_length = mqArray.length;
                var submq = function(){
                    mqC.style.right = mqArray[counter++];
                    if(counter >= mqArray_length)counter=0;
                }
            }else{
                for(var i=0 ; counter<wid ; counter+=r, i++){
                    mqArray[i] = counter + 'px';
                    mqArrayZ[i] = zz(i);
                }
                counter = 0;
                mqArray_length = mqArray.length;
                var submq = function(){
                    mqC.style.right = mqArray[counter];
                    mqC.style.top = mqArrayZ[counter];
                    counter++;
                    if(counter >= mqArray_length)counter=0;
                }
            }
            break;
    }
    itv[itv.length] =  setInterval(submq, t);
    if(m){
        mqC./*@if (@_jscript) attachEvent ('on' + @else@*/
         addEventListener ( /*@end@*/ 'mouseover', function(itv_len){
             return function(){clearInterval(itv[itv_len])}
        }(itv.length-1), false );
        
        mqC./*@if (@_jscript) attachEvent ('on' + @else@*/
         addEventListener ( /*@end@*/ 'mouseout', function(submq_,t_,itv_len){
            return function(){
                itv[itv_len] = setInterval(submq_, t_);
            }
        }(submq,t,itv.length-1), false );
    }
    
    //参照を切っておく
    mqCoffsetH = null;
    mqCoffsetW = null;
    wid = null;
    hei = null;
    sv_mqC = null;
    mqP = null;
}
//-->
</script>
使用例
動かしたいタグの class を、mq〜で指定するだけです。mqABC とか mq1 とか mq とか。
例: <span class="mqMessage">はやい! やすい!! うまい!!! 男なら高カロリー。</span>
はやい! やすい!! うまい!!! 男なら高カロリー。
このままだと左から右へ動きますが、動く方向、動く早さ等はパラメータで指定することもできます。
パラメータは mq〜の後に "_" を区切り文字として指定します。
_d が方向、_s がスピード、_w が幅、_h が高さ、_z が秘密のスイッチです。

☆動く方向を指定する場合
例: <u class="mqMessage_dright">はやい! やすい!! うまい!!! 男なら高カロリー。</u>
はやい! やすい!! うまい!!! 男なら高カロリー。
この例では右方向に動きます。方向は、right、left、top、bottom の4種類指定できます。

☆スピードを調整したい場合
例: <b class="mqMessage_s1_dtop">はやい! やすい!! うまい!!! 男なら高カロリー。</b>
はやい! やすい!! うまい!!! 男なら高カロリー。
スピードは 1 〜 5 の範囲で指定できます。何も指定しない時のスピードは 4 になっています。

☆動作範囲の幅や高さを調整したい場合
例: <em class="mqMessage_w100">はやい! やすい!! うまい!!! 男なら高カロリー。</em>
はやい! やすい!! うまい!!! 男なら高カロリー。
幅は _w 高さは _h で指定します。指定しなければもともとの文字や画像の幅です。

☆秘密のスイッチを使いたい場合
例: <img class="mqMessage_z_w200" src="madeonamac.gif" alt="madeonamac">
madeonamac
_z を指定することでウネウネ(ポコポコ)します。
ここでは本スクリプトが画像にも直接使えることを示し感動を高めています。

☆マウスオーバーで止まり、離すと再度動き出すようにする場合(2009.09追加)
例:<span class="mqMessage_w300_m">(゚o゚;)ニゲロー</span>
(゚o゚;)ニゲロー
_m を指定することで、マウスが重なった時に一時停止するようになります。

☆最後に
パラメータの指定に大文字小文字は関係ありません。設定順序も自由です。
本スクリプトは既存 HTML 内の onload 等と干渉しません。
よくある onload プロパティをスクリプトで指定するやり方は html 側の onload と干渉する危険があります。
また本スクリプトは既存のスタイル・レイアウトを崩しません(たぶん)。
例えば absolute で指定した要素も位置を崩すことなく動作させます。
ちなみに class は半角スペース区切りで複数指定できるので(例: class="aaa bbb")そういった意味でもスタイルを崩しません。
目立たないよう最後に弱点を書きますが、td 要素に指定すると何かおかしくなることが多いです。
直接 td には指定せず、中に div でも入れてそこで指定して下さい。
それから、本家 marquee はインラインですがこれはロジック的にブロックになります。
もっと弱点はありますがあとは秘密です。
苦情、お待ちしております。(→掲示板

ご意見・ご指摘等ございましたらお願い致します。(雑記帳へ
ホームページへ戻る