1. 程式人生 > >【原生JS元件】javascript 運動框架

【原生JS元件】javascript 運動框架

大家都知道JQuerry有animate方法來給DOM元素進行運動,CSS3中也有transition、transform來進行運動。而使用原生的Javascript來控制元素運動,需要寫很多運動的細節以及相容。

然而,當你的BOSS不讓你使用龐大的JQ框架,而且你開發的產品也需要在一些不相容CSS3的瀏覽器執行的時候,你是否覺得每次都要開個定時器來琢磨運動該怎麼進行,是件很費力的事情呢?

那麼福利來了,筆者最近總結了兩個常用的運動框架,並將其寫成元件,
只要按照下面的方法呼叫,即可方便使用。

1.在你的頁面中引入js

<script src="Mover.js"></script
>

2.在你的js程式碼中建立Mover物件

<script>
window.onload=function(){
    var mover = new Mover();
};
</script>

3.開始使用mover!
用法說明:筆者寫的元件包含了兩種運動框架供使用,一種是基於速度的;一種是基於時間的,讓我們來先看看基於速度的運動框架的用法

startMoveBySpeed(obj, json, endFn);

引數obj : 傳入要運動的物件

引數json : 以json的形式傳入要運動的屬性,包括left、top、width、height等以px為單位的屬性和透明度opacity,他們的值是運動的終點

引數endFn(可選) : 結束運動後要執行的方法

<script>
//基於速度的運動框架用法
window.onload=function(){
    //得到你要運動的元素
    var oDiv = document.getElementsByTagName('div')[0];

    //使用運動框架
    var mover = new Mover();
    oDiv.onclick=function(){    
        mover.startMoveBySpeed(this,{'left':200,'width':300,'opacity':50});
    //使oDiv的left運動到200px,寬度變為300px,透明度變為50%
} };
</script>

讓我們來看看另外一種基於時間的運動框架

startMoveByTime(obj, json, options, endFn )

引數obj : 傳入要運動的物件

引數json : 以json的形式傳入要運動的屬性,包括left、top、width、height等以px為單位的屬性和透明度opacity,他們的值是運動的終點

引數options : 以json的形式傳入傳入運動的總時間和運動方式,如:
{
‘times’ : 1000,//運動總時間為1s
‘fx’ : ‘linear’ // 運動形式為勻速
}
當options傳入引數為空json{}時,就使用預設設定(運動時間500ms,運動形式為勻速)

引數endFn(可選) : 結束運動後要執行的方法

//基於事件的運動框架用法
window.onload=function(){
    //得到你要運動的元素
    var oDiv = document.getElementsByTagName('div')[0];
    //使用運動框架
    var mover = new Mover();
    oDiv.onclick=function(){
        mover.startMoveByTime(
            this, 
            {'left':500,'top':200}, 
            {'times':1000,'fx':'easeIn'}, 
            function(){
                mover.startMoveByTime(this,{'opacity':20},{});
        });
        //使oDiv的left變為500px,高度變為200px,結束上述運動後再使透明度變為20%
    }
}

現在來說說兩種方式的區別吧,使用第一種方式進行運動時,元素的各項需要改變的屬性的運動速度相同,而由於每項屬性起點到終點的距離不一樣,所以各項屬性到達運動終點的時間也不一樣。而第二種運動直接固定了運動總時間相同,所以所有傳入引數的屬性一起改變、一起終止。

以下是Mover.js元件的程式碼,筆者乃菜鳥一隻,歡迎指正,後面有時間也會將彈性運動、碰撞運動、重力運動一起寫入框架

/*
    js原生運動元件
*/

//Mover建構函式
function Mover(){
    this.setting = {
        'times' : 500,
        'fx' : 'linear'
    }
}

//獲取當前樣式
Mover.prototype.getStyle = function(obj,attr)
{
    return obj.currentStyle ? obj.currentStyle[attr] : getComputedStyle(obj)[attr];
}

//獲取當前時間
Mover.prototype.now = function(){
        return (new Date()).getTime();
}

//速度版運動框架
Mover.prototype.startMoveBySpeed = function (obj,json,fnEnd)
{
    clearInterval(obj.timer);
    _this = this;
    obj.timer = setInterval(function(){
        obj.bStop = true;
        for(var attr in json)
        {
            var cur = 0;
            var speed = 0;
            if(attr === 'opacity')
            {
                cur = _this.getStyle(obj,attr);
                cur = Math.round( parseFloat(cur*100) );
            }
            else
            {
                cur = parseInt(_this.getStyle(obj,attr));
            }

            var speed = (json[attr]-cur)/8;

            speed = speed ? Math.ceil(speed):Math.floor(speed);

            if(cur !== json[attr])
            {
                obj.bStop = false;
            }

            if(attr === 'opacity')
            {
                obj.style.opacity = (cur+speed)/100;
                obj.style.filter = 'Alpha(opacity:'+(cur+speed)+')';
            }
            else
            {
                obj.style[attr] = (cur+speed)+'px';
            }
        }

        if(obj.bStop)
        {
            clearInterval(obj.timer);
            fnEnd && fnEnd.call(obj);
        }

    },20);
}

//時間版運動框架
Mover.prototype.startMoveByTime = function(obj,json,options,endFn){
    //對於時間版框架來說,初始值b是固定的,所以寫在定時器外面
    var _this = this;
    //預設設定
    extend(_this.setting,options);

    var iCur = {};
    //獲取當前值
    for(attr in json)
    {
        iCur[attr] = 0;

        if(attr === 'opacity')
        {
            iCur[attr] = Math.round( parseFloat( _this.getStyle(obj,attr) )*100 );
        }else{
            iCur[attr] = parseInt( _this.getStyle(obj,attr) );
        }

    };

    var iStartTime = _this.now();
    clearInterval(obj.timer);
    obj.timer = setInterval(function(){
        var iCurTime = _this.now();
        var t = _this.setting.times-
        Math.max(0,iStartTime-iCurTime+_this.setting.times);
        for(attr in json)
        {   
            /*
                Tween[fx]函式4個引數
                t:current  time(當前時間)
                b:beginning  value(初始值)
                c: change  in  value(變化量)
                d:duration(持續時間)
                return  (目標點)   
            */
            var value = _this.Tween[_this.setting.fx](
                t,                      //t  0~times
                iCur[attr],             //b
                json[attr]-iCur[attr],  //c
                _this.setting.times                     //d
             );
            if(attr === 'opacity')
            {
                obj.style[attr] =  parseFloat(value/100);
                obj.style.filter = 'alpha(opacity:'+value+')';
            }else{
                obj.style[attr] = value + 'px';
            }

        }

        if( t === _this.setting.times )
        {
            clearInterval(obj.timer);
            endFn && endFn.call(obj);
        }
    },13);

}
//覆蓋預設設定
function extend(child,father){
    for(var attr in father)
    {
        child[attr] = father[attr];
    }
}
//Tween運動演算法
Mover.prototype.Tween = {
    /*
        4個引數
        t:current  time(當前時間)
        b:beginning  value(初始值)
        c: change  in  value(變化量)
        d:duration(持續時間)
        return  (目標點)

    */

    linear: function (t, b, c, d){  //勻速
        return c*t/d + b;
    },
    easeIn: function(t, b, c, d){  //加速曲線
        return c*(t/=d)*t + b;
    },
    easeOut: function(t, b, c, d){  //減速曲線
        return -c *(t/=d)*(t-2) + b;
    },
    easeBoth: function(t, b, c, d){  //加速減速曲線
        if ((t/=d/2) < 1) {
            return c/2*t*t + b;
        }
        return -c/2 * ((--t)*(t-2) - 1) + b;
    },
    easeInStrong: function(t, b, c, d){  //加加速曲線
        return c*(t/=d)*t*t*t + b;
    },
    easeOutStrong: function(t, b, c, d){  //減減速曲線
        return -c * ((t=t/d-1)*t*t*t - 1) + b;
    },
    easeBothStrong: function(t, b, c, d){  //加加速減減速曲線
        if ((t/=d/2) < 1) {
            return c/2*t*t*t*t + b;
        }
        return -c/2 * ((t-=2)*t*t*t - 2) + b;
    }
}