1. 程式人生 > >js勻速動畫及動畫庫

js勻速動畫及動畫庫

準備知識
1.定時器

使用定時器分為兩步:設定定時器、設定等待時間

window.setInterval([function],[interval]); //設定一個定時器,到達指定的時間,每隔這麼長時間都重複執行[function]
window.setTimeOut([function],[interval]); //只執行一次

定時器返回的是一個數字,代表當前是第幾個定時器
清除定時器:

window.clearTimeOut(1); //數字表示清除掉第幾個
window.clearInterval(timer2);
2.獲取螢幕的寬高(相容所有瀏覽器)
var curWidth = document.documentElement
.clientWidth || document.body.clientWidth; //獲取當前螢幕的寬度 var curHeight = document.documentElement.clientHeight || document.body.clientHeight; //獲取當前螢幕的高度
1.在指定時間內實現單方向勻速運動

讓藍色方塊向右勻速運動,2s後到達螢幕右側,每10ms走一步,如圖所示:
這裡寫圖片描述
(1)通過計算步長來實現:
示意圖:
這裡寫圖片描述

<script type="text/javascript">
    var oBox = document.getElementById("box"
); //盒子走的距離: var maxLeft = (document.documentElement.clientWidth || document.body.clientWidth) - oBox.offsetWidth; //運動的時間為2s var duration = 2000; //步長=距離/時間*10 var step = (maxLeft / duration) * 10; var timer = window.setInterval(function () { var curLeft = utils.css(oBox, "left"
); //獲取 if (curLeft >= maxLeft) { window.clearInterval(timer); return; //不加會有滾動條 } curLeft += step; utils.css(oBox, "left", curLeft); //設定 }, 10);
</script>

(2)通過計算每次運動的距離來實現:
這裡寫圖片描述

<script type="text/javascript">
    var oBox = document.getElementById('box'); //獲取id是box的div元素
    var duration = 2000; //運動的總時間為2s
    //走的總距離/應該走的距離
    var target = (document.documentElement.clientWidth || document.body.clientWidth) - oBox.offsetWidth;
    var begin = utils.css(oBox, 'left'); //獲取起始位置
    var change = target - begin; //剩下的距離
    var interval = 10; //每10毫秒執行一次
    var time = null;
    var timer = window.setInterval(function () { //建立定時器
        time += interval;
        if (time >= duration) { //當累加時間大於2秒時停止定時器
            window.clearInterval(timer);
            oBox.style.left = target + 'px'; //最後差一點,加這段程式碼補上最後的距離
            return;
        }
        oBox.style.left = begin + (time / duration) * change + 'px'; //現在運動的位置
    }, interval);
</script>
2.在指定步長內實現單方向勻速運動

讓藍色方塊向右勻速運動,步長為5,到達螢幕右側
注意邊界問題:

<script type="text/javascript">
    var oBox = document.getElementById("box");
    //盒子走的距離:
    var maxLeft = (document.documentElement.clientWidth || document.body.clientWidth) - oBox.offsetWidth;
    var step = 5;
    var timer = window.setInterval(function () {
        var curLeft = utils.css(oBox, "left");
        if (curLeft+step >= maxLeft) { //在進行邊界判斷的時候加上步長來進行處理
            utils.css(oBox,"left",maxLeft);
            window.clearInterval(timer);
            return; //直接結束
        }
        curLeft += step;
        utils.css(oBox, "left", curLeft);
    }, 10);
</script>
3.使用setTimeout以及遞迴的方法實現

使用setTimeout設定動畫,每一次在執行動畫之前首先把上一次設定的那個沒用的定時器清除掉,節約記憶體空間

<script type="text/javascript">
    var oBox = document.getElementById("box");
    //盒子走的距離:
    var maxLeft = (document.documentElement.clientWidth || document.body.clientWidth) - oBox.offsetWidth;
    var step = 5;
    var timer = null; //接收定時器的返回值,設定成全域性變數
    function move() {
        window.setTimeout(timer);
        var curLeft = utils.css(oBox, "left");
        if (curLeft + step >= maxLeft) { //在進行邊界判斷的時候加上步長來進行處理
            utils.css(oBox, "left", maxLeft);
            return; //直接結束
        }
        curLeft += step;
        utils.css(oBox, "left", curLeft);
        timer = window.setTimeout(move, 10);
    }
    move();
</script>
4.反彈運動動畫

寫兩個按鈕,控制著方塊向左和向右,如圖:
這裡寫圖片描述

<script type="text/javascript">
    var oBox = document.getElementById("box");
    var minLeft = 0;
    var maxLeft = (document.documentElement.clientWidth || document.body.clientWidth) - oBox.offsetWidth;
    var timer = null;
    function move(target) { //target:運動的目標位置
        window.clearTimeout(timer);
        var curLeft = utils.css(oBox, "left");
        if (curLeft < target) { //向右
            if (curLeft + 5 >= target) {
                utils.css(oBox, "left", target);
                return;
            }
            curLeft += 5;
            utils.css(oBox, "left", curLeft);
        } else if (curLeft > target) { //向左
            if (curLeft - 5 <= target) {
                utils.css(oBox, "left", target);
                return;
            }
            curLeft -= 5;
            utils.css(oBox, "left", curLeft);
        } else { //不需要動
            return;
        }
        timer = window.setTimeout(function () {
            move(target);
        }, 10);
    }
    document.getElementById("btn_left").onclick = function () {
        move(minLeft);
    };
    document.getElementById("btn_right").onclick = function () {
        move(maxLeft);
    };
</script>

但是這樣寫效能不好,因為每一次到達時間的時候,都要先執行一次匿名函式,形成一個私有作用域,在匿名函式中再執行move。但是move中需要用到的資料值在第一次執行的move方法中,需要把匿名函式形成的這個私有作用域作為跳板找到之前的,這樣就導致了匿名函式形成的這個私有作用域不能銷燬。
解決方法:可以在move()中巢狀一個_move()方法

<script type="text/javascript">
    var oBox = document.getElementById("box");
    var minLeft = 0;
    var maxLeft = (document.documentElement.clientWidth || document.body.clientWidth) - oBox.offsetWidth;
    var timer = null;
    function move(target) { //target:運動的目標位置
        _move();
        function _move() {
            window.clearTimeout(timer);
            var curLeft = utils.css(oBox, "left");
            if (curLeft < target) { //向右
                if (curLeft + 5 >= target) {
                    utils.css(oBox, "left", target);
                    return;
                }
                curLeft += 5;
                utils.css(oBox, "left", curLeft);
            } else if (curLeft > target) { //向左
                if (curLeft - 5 <= target) {
                    utils.css(oBox, "left", target);
                    return;
                }
                curLeft -= 5;
                utils.css(oBox, "left", curLeft);
            } else { //不需要動
                return;
            }
            timer = window.setTimeout(_move, 10);
        }
        timer = window.setTimeout(function () {
            move(target);
        }, 10);
    }
    document.getElementById("btn_left").onclick = function () {
        move(minLeft);
    };
    document.getElementById("btn_right").onclick = function () {
        move(maxLeft);
    };
</script>
5.多方向勻速運動

top和left同時改變
這裡寫圖片描述

<script type="text/javascript">
    function Linear(t, b, c, d) {
        return c * t / d + b;
    }
    var oBox = document.getElementById("box");
    var beginLeft = utils.css(oBox, "left"), beginTop = utils.css(oBox, "top");
    var targetLeft = 1000, targetTop = 500;
    var changeLeft = targetLeft - beginLeft, changeTop = targetTop - beginTop;
    var duration = 1000, time = 0;
    oBox.timer = window.setInterval(function () {
        time += 10;
        if (time >= duration) {
            utils.css(oBox, {
                left: targetLeft,
                top: targetTop
            });
            window.clearInterval(oBox.timer);
            return;
        }
        var curLeft = Linear(time, beginLeft, changeLeft, duration);
        var curTop = Linear(time, beginTop, changeTop, duration);
        utils.css(oBox, {
            left: curLeft,
            top: curTop
        });
    }, 10);
</script>
6.建立運動動畫庫
~function () {
    var tweenEffect = {
        //勻速
        Linear: function (t, b, c, d) {
            return c * t / d + b;
        },

        //指數衰減的反彈緩動
        Bounce: {
            easeIn: function (t, b, c, d) {
                return c - tweenEffect.Bounce.easeOut(d - t, 0, c, d) + b;
            },
            easeOut: function (t, b, c, d) {
                if ((t /= d) < (1 / 2.75)) {
                    return c * (7.5625 * t * t) + b;
                } else if (t < (2 / 2.75)) {
                    return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
                } else if (t < (2.5 / 2.75)) {
                    return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
                } else {
                    return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
                }
            },
            easeInOut: function (t, b, c, d) {
                if (t < d / 2) {
                    return tweenEffect.Bounce.easeIn(t * 2, 0, c, d) * .5 + b;
                }
                return tweenEffect.Bounce.easeOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
            }
        },

        //二次方的緩動
        Quad: {
            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;
            },
            easeInOut: function (t, b, c, d) {
                if ((t /= d / 2) < 1) {
                    return c / 2 * t * t + b;
                }
                return -c / 2 * ((--t) * (t - 2) - 1) + b;
            }
        },

        //三次方的緩動
        Cubic: {
            easeIn: function (t, b, c, d) {
                return c * (t /= d) * t * t + b;
            },
            easeOut: function (t, b, c, d) {
                return c * ((t = t / d - 1) * t * t + 1) + b;
            },
            easeInOut: function (t, b, c, d) {
                if ((t /= d / 2) < 1) {
                    return c / 2 * t * t * t + b;
                }
                return c / 2 * ((t -= 2) * t * t + 2) + b;
            }
        },

        //四次方的緩動
        Quart: {
            easeIn: function (t, b, c, d) {
                return c * (t /= d) * t * t * t + b;
            },
            easeOut: function (t, b, c, d) {
                return -c * ((t = t / d - 1) * t * t * t - 1) + b;
            },
            easeInOut: 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;
            }
        },

        //五次方的緩動
        Quint: {
            easeIn: function (t, b, c, d) {
                return c * (t /= d) * t * t * t * t + b;
            },
            easeOut: function (t, b, c, d) {
                return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
            },
            easeInOut: function (t, b, c, d) {
                if ((t /= d / 2) < 1) {
                    return c / 2 * t * t * t * t * t + b;
                }
                return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
            }
        },

        //正弦曲線的緩動
        Sine: {
            easeIn: function (t, b, c, d) {
                return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
            },
            easeOut: function (t, b, c, d) {
                return c * Math.sin(t / d * (Math.PI / 2)) + b;
            },
            easeInOut: function (t, b, c, d) {
                return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
            }
        },

        //指數曲線的緩動
        Expo: {
            easeIn: function (t, b, c, d) {
                return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
            },
            easeOut: function (t, b, c, d) {
                return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
            },
            easeInOut: function (t, b, c, d) {
                if (t == 0) return b;
                if (t == d) return b + c;
                if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
                return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
            }
        },

        //圓形曲線的緩動
        Circ: {
            easeIn: function (t, b, c, d) {
                return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
            },
            easeOut: function (t, b, c, d) {
                return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
            },
            easeInOut: function (t, b, c, d) {
                if ((t /= d / 2) < 1) {
                    return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
                }
                return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
            }
        },

        //超過範圍的三次方緩動
        Back: {
            easeIn: function (t, b, c, d, s) {
                if (s == undefined) s = 1.70158;
                return c * (t /= d) * t * ((s + 1) * t - s) + b;
            },
            easeOut: function (t, b, c, d, s) {
                if (s == undefined) s = 1.70158;
                return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
            },
            easeInOut: function (t, b, c, d, s) {
                if (s == undefined) s = 1.70158;
                if ((t /= d / 2) < 1) {
                    return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
                }
                return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
            }
        },

        //指數衰減的正弦曲線緩動
        Elastic: {
            easeIn: function (t, b, c, d, a, p) {
                if (t == 0) return b;
                if ((t /= d) == 1) return b + c;
                if (!p) p = d * .3;
                var s;
                !a || a < Math.abs(c) ? (a = c, s = p / 4) : s = p / (2 * Math.PI) * Math.asin(c / a);
                return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
            },
            easeOut: function (t, b, c, d, a, p) {
                if (t == 0) return b;
                if ((t /= d) == 1) return b + c;
                if (!p) p = d * .3;
                var s;
                !a || a < Math.abs(c) ? (a = c, s = p / 4) : s = p / (2 * Math.PI) * Math.asin(c / a);
                return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b);
            },
            easeInOut: function (t, b, c, d, a, p) {
                if (t == 0) return b;
                if ((t /= d / 2) == 2) return b + c;
                if (!p) p = d * (.3 * 1.5);
                var s;
                !a || a < Math.abs(c) ? (a = c, s = p / 4) : s = p / (2 * Math.PI) * Math.asin(c / a);
                if (t < 1) return -.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
                return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
            }
        }
    };

    function move(curEle, target, duration, effect, callback) { //讓哪個元素運動
        var tempEffect = tweenEffect.Linear;
        if (typeof effect == 'number') {
            switch (effect) {
                case 0:
                    tempEffect = tweenEffect.Linear;
                    break;
                case 1:
                    tempEffect = tweenEffect.Bounce.easeIn;
                    break;
                case 2:
                    tempEffect = tweenEffect.Quart.easeInOut;
            }
        } else if (effect instanceof Array) {
            tempEffect = effect.length >= 2 ? tweenEffect[effect[0]][effect[1]] : tweenEffect[effect[0]];
        } else if (typeof effect == 'function') { //用來做回撥函式
            callback = effect;
        }


        //根據target獲取每一個方向的起始值和總距離
        //首先清除定時器
        window.clearInterval(curEle.tweenTimer);
        var begin = {};
        var change = {};
        var time = 0;
        var interval = 10;
        for (var key in target) {
            if (target.hasOwnProperty(key)) {
                begin[key] = utils.getCss(curEle, key);
                change[key] = target[key] - begin[key];
            }
        }
        curEle.tweenTimer = window.setInterval(function () {
            time += interval;
            if (time >= duration) { //到達目標
                window.clearInterval(curEle.tweenTimer);
                utils.setGroupCss(curEle, target);
                typeof callback == 'function' && callback.call(curEle); //回撥函式判斷
                return;
            }
            //沒到達目標,分別獲取每個方向,然後並且設定
            for (var key in target) {
                if (target.hasOwnProperty(key)) {
                    var curPosi = tempEffect(time, begin[key], change[key], duration);
                    utils.setCss(curEle, key, curPosi);
                }
            }
        }, interval);
    }

    //可以用return返回出來個一個物件,或者直接賦值給window
    window.tweenAnimate = move;

}();