JavaScript封裝簡易動畫函數

  在學習javascript動畫效果的過程,動畫函數一定是少不了的,所以在初級學習的過程中,封裝好一個動畫函數可以直接調用能夠幫我們省下更多的學習時間。下面是我一步步完善動畫函數的過程。
1、簡單的右移函數:鼠標點擊按鈕,box向右移動一定的位置
//封裝右移動畫函數
        function animateMoveRight(element,target){
            //通過offsetLeft獲取當前位置的left值
            var left = element.offsetLeft;

            //開啟定時器
            var timer = setInterval(function(){
                //當前位置每一次+5 實現勻速運動
                left += 5;
                //將每一次改變后的left值設置給元素
                element.style.left = left + 'px';
                //判斷動畫結束的標志 到達目標位置停止計時器
                if(left >= target){
                    clearInterval(timer);
                }    
            },30);
        }
        //在window.onload中調用封裝好的動畫函數
        window.onload =function () {
            var box = document.getElementById('box');

            //動態創建按鈕
            for(var i = 200; i < 1000; i += 100){
                //創建按鈕
                var button = document.createElement('button');            
                button.innerHTML = '右移' + i + 'px';
                //獲取按鈕對應的移動值
                button.index = i;
                //點擊按鈕
                button.onclick = function(){
                    // console.log(this.index);
                    //調用封裝好的右移函數
                    animateMoveRight(box,this.index);
                }
                //添加按鈕
                document.body.appendChild(button);
            }            
        }
    </script>

實現效果:


右移效果.png
2、封裝同時解決左右移動的動畫函數

上面的函數只能實現單向的向右移動,點擊‘右移500px’按鈕后,再點擊‘右移200px’按鈕無法回到200px處。下面實現這種效果。

//封裝一個函數  同時解決左右移動的問題
        function animateMove(element,target){
            clearInterval(timer)
            var left = element.offsetLeft;

            //設置步長 表示一步動作的差值
            //通過比較element當前的left值和target值的大小,來確定平移方向
            var step = (target - left) / 10;

            var timer = setInterval(function(){
                //如果目標值大于當前的left值,step為正數,向右移動
                //如果目標值小于當前的left值,step為負數,向左移動
                left += step;
                box.style.left = left + 'px';

                //判斷停止動畫
                //比較差值,取絕對值,當兩者的差值小于了步進值時,停止動畫,
                if(Math.abs(target - left) <= Math.abs(step)){
                    clearInterval(timer);
                    element.style.left = target + 'px';
                }    
            },30);
        }

        
        //調用函數
        window.onload =function () {
            var box = document.getElementById('box');
            
            for(var i = 200; i < 1000; i += 100){
                var button = document.createElement('button');    

                button.innerHTML = '右移' + i + 'px';
                button.target = i;
                button.onclick = function(){
                    animateMove(box,this.target);
                }
                document.body.appendChild(button);
            }            
        }

效果展示:(當前在400px位置,點擊200px按鈕會回到200px位置)

左右移動.png
3、封裝帶有指定屬性的動畫函數

在設置動畫的過程中,不只是左右移動那么簡單,我們想要是的是想改變元素的什么屬性就能夠改變。在開始之前先了解怎么訪問并獲取css的屬性。

3.1 訪問css屬性

我們知道element.style.xxx 只能夠獲取行內式屬性,無法獲取在style中設置的屬性;offset獲取的是本身實際獲得的,沒有定位,沒有設left夜里可以獲得。我們通過設置element.currentStyle.xxx(ie瀏覽器)或者window.getComputedStyle(element, null).xxx(其他瀏覽器)獲得。封裝函數如下:

1 //封裝一個函數,用于獲取某一個元素的某一條CSS屬性值
 2         function getStyle(element, styleName){
 3             if(element.currentStyle){
 4                 return element.currentStyle[styleName];
 5             }else{
 6                 var computedStyle = window.getComputedStyle(element, null);
 7                 return computedStyle[styleName];
 8             }
 9         }
10 
11         window.onload = function(){
12             var box = document.getElementById('box');
13             var height = getStyle(box,'height');
14             console.log(height); //200px
15         }
3.2 封裝帶有指定屬性的動畫函數
//引入getStyle函數
        function getStyle(element, styleName){
            if(element.currentStyle){
                return element.currentStyle[styleName];
            }else{
                var computedStyle = window.getComputedStyle(element, null);
                return computedStyle[styleName];
            }
        }

        //封裝帶有指定屬性的的動畫函數 (元素名,屬性名,目標值)
        var timer;
        function animate(element,styleName,target){
            clearInterval(timer);
            //獲取該元素當前的屬性
            var current = parseInt(getStyle(element,styleName));
            //設置步長 定值
            var step = (target - current) / 10;
            
            //開啟動畫設置滾動效果移動 
            timer = setInterval(function(){
                //通過步長 一點的的改變current  直到達到target值
                current += step;
                //判斷動畫結束的標志 
                //比較差值 當兩者的差值小于了步進值時,停止動畫 
                if(Math.abs(target - current) <= Math.abs(step)){
                    clearInterval(element.timer);
                    current = target;  //存在一點誤差 強制將current歸為目標值
                }

                //將改變后當前動畫中的style值,設置給動畫的元素
                element.style[styleName] = current + 'px';
            },30);
        }

        //調用
        window.onload = function(){
            var box =document.getElementById('box');
            
            animate(box,'width',500);
        } 
4、封裝帶有多個屬性的動畫函數(同時運動)

前面雖然能根據傳入的屬性參數改變元素運動,但是每次只能設置一種屬性,如果同時調用只會顯示最后一種效果。所以下面使用json參數傳入多個屬性。

 <script type="text/javascript">
    //json格式參考
    function f(){
        var json ={left:100,top:50}
        for(var key in json){
            console.log(key); //打印屬性 left top
            console.log(json[key]); //打印屬性值 100  50
        }
    }
    f();
    </script>
//獲取屬性的的網頁中實際的(當前的)屬性值
        function getStyle(element, styleName){
            if(element.currentStyle){
                return element.currentStyle[styleName];
            }else{
                var computedStyle = window.getComputedStyle(element, null);
                return computedStyle[styleName];
            }
        }

        //封裝帶有多個屬性的的動畫函數    利用json參數
        function animate(element,json){
            clearInterval(element.timer);
            //由于多個屬性的運動  為了避免一個屬性完成后就停止定時器的現象,所以設置isStop
            //是否停止動畫,默認為false表示不停止
            var isStop = false;

            //開啟動畫設置滾動效果移動 
            element.timer = setInterval(function(){

                //1.每一次動畫開啟之前,默認設置isStop為true(定時器停止)
                //2.如果只是一個屬性完成不需要修改定時器,如果有屬性沒有    執行完,則設置isStop = false,繼續開啟定時器
                //3.最后所有屬性都完成后, 判斷isStop值     如果為true,表示的屬性均執行完成,關閉定時器
                //1.
                isStop = true;

                //多個屬性  分別計算每個屬性當前值(實際值)/目標值/步長
                //遍歷json參數  分別獲取key-屬性名 json[key]-屬性值
                for(var key in json){
                    console.log(key);        //left top
                    console.log(json[key]);  // 100 50

                    //通過getStyle函數獲取當前屬性(key)的屬性值即盒子的當前實際值
                    var current = parseInt(getStyle(element, key));
                    //獲取json參數傳入的每個屬性對應的目標值
                    var target = json[key];
                    //分別設置每個屬性步長
                    var step = (target - current) / 10;
                    step = step > 0 ? Math.ceil(step) : Math.floor(step);

                    //設置一步步的改變 直至達到目標值
                    current += step;

                    //判斷(current += step)是否達到目標值 停止計時器
                    //2.其中一個屬性完成,就不需要修改定時器
                    if(Math.abs(target -current) > Math.abs(step)){
                        isStop = false;
                    }else{ //強制將此屬性設到target
                        current = target;
                    }

                    //設置運動后的值給元素, 改變其對應屬性的屬性值
                    element.style[key] = current + 'px';    
                }

                //3.所有的屬性動畫完成(for(key)結束),所有的定時器都為true,關閉定時器
                if(isStop){
                    clearInterval(element.timer);
                    console.log('完成動畫');
                }
            },30);
        }
        //調用此函數
        window.onload = function(){
            var box =document.getElementById('box');
            document.onclick =function(){
                //實現點擊后,在一定時間內同時完成以下動作
                animate(box,{
                    left:200,
                    top :200,
                    width:300,
                    height:300
                });
            }
        }

實現效果:鼠標點擊后,在一定時間內盒子同時向左向下移動200px,并且寬高擴大到300px。

5、函數的回歸調用(上一個動畫運動完成后下一個動畫才開始運動)

上面的代碼實現了一個物體的多個屬性同時運動,很多情況下會是一個物體的上一個動畫完成后另一個動畫才開始運動。所以我們在原來的基礎上傳入一個函數參數,function animate(element,json,fun){},在上一個動畫完成后,開始調用下一個動畫的函數參數。

  //回調函數
        function animate(element,json,fun){
            clearInterval(element.timer);
            console.log(element.offsetLeft + 'kaishiqian')
            var isStop = false;

            element.timer = setInterval(function(){

                isStop = true;

                for(var key in json){
                    console.log(key);        //left top
                    console.log(json[key]);  // 100 50
                    var current = parseInt(getStyle(element, key));
                    var target = json[key];
                    var step = (target - current) / 10;
                    step = step > 0 ? Math.ceil(step) : Math.floor(step);
                    current += step;

                    if(Math.abs(target -current) > Math.abs(step)){
                        isStop = false;
                    }else{ //強制將此屬性設到target
                        current = target;
                    }
                    element.style[key] = current + 'px';    
                }

                if(isStop){
                    clearInterval(element.timer);
                    console.log('完成動畫');
                    console.log(element.offsetLeft);

                    //上一個動畫完成后,開始下一個動畫
                    if(typeof fun == 'function'){
                        fun();
                    }
                }
            },30);
        }

        window.onload = function(){
            var box =document.getElementById('box');
            document.onclick =function(){
                //先向右移動到500px,接著寬高均擴大到300px,下移到150px,字體放大到30px
                animate(box,{left:500}, function(){
                    animate(box,{width:300, height:300}, function(){
                        animate(box,{top:150}, function(){
                            animate(box,{fontSize:30}, null);
                        });
                    });
                });
            }
        }

實現效果:鼠標點擊后,盒子先向右移動到500px,接著寬高均擴大到300px,下移到150px,字體放大到30px。

6、封裝帶有opacity、z-index等屬性的動畫函數
//思路:即查看current,target,step的值是否會因為opacity的傳入而出錯
        function animate(element,json,fun){
            clearInterval(element.timer);
            console.log(element.offsetLeft + 'kaishiqian')
            var isStop = false;

            element.timer = setInterval(function(){

                isStop = true;

                for(var key in json){

                    var current;
                    //如果傳入的屬性是opacity,取浮點型數
                    if(key == 'opacity'){
                        current = parseFloat(getStyle(element, key));
                    }else{
                        current = parseInt(getStyle(element, key));
                    }

                    //沒問題
                    var target = json[key];

                    //只要不是opacity 都做向上或向下取整操作
                    var step = (target - current) / 10;
                    if(key != 'opacity'){
                        step = step > 0 ? Math.ceil(step) : Math.floor(step);
                    }

                    current += step;

                    //判斷暫停動畫
                    if(key == 'opacity'){

                        if(Math.abs(target -current) > 0.01){
                            isStop = false;
                        }else{ 
                            current = target;
                        }
                        element.style[key] = current + '';

                    }else{

                        if(Math.abs(target -current) > Math.abs(step)){
                            isStop = false;
                        }else{ //強制將此屬性設到target
                            current = target;
                        }
                        
                        if(key == 'zIndex'){
                            //四舍五入
                            element.style.zIndex = Math.round(current);
                        }else{
                            element.style[key] = current + 'px';    
                        }
                        
                    }
                }
                if(isStop){
                    clearInterval(element.timer);
                    console.log('完成動畫');
                    if(typeof fun == 'function'){
                        fun();
                    }
                }
            },30);
        }

        window.onload = function(){
            var box =document.getElementById('box');
            document.onclick =function(){

                animate(box, {opacity:0.3,zIndex:20}, null);
            }
        }

實現效果:透明度由1 變為0.3 ,z-index由10 變為20。

到這里一個較為完善的動畫函數就封裝完成了。

如有任何疑問請留言。

接下將介紹幾種通過js動畫效果實現各式輪播圖的案例......

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,588評論 25 707
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,147評論 4 61
  • ˙?˙ ? ?? ? ?????? ′? ? `? ?_? (-?_-?) ??...
    邢月婷閱讀 9,842評論 0 1
  • O:早上晨會,領導分享了柯達的故事: 柯達之前是行業的巨霸,由于膠卷利潤率太可觀等一些原因,高層把新技術隱藏起來,...
    海洋里的小彩魚閱讀 181評論 0 0
  • 感冒發燒鼻塞咳嗽簡直就是這個季節的常態。雖然我們都知道這些問題不大,很快就會好;雖然我們都懂小朋友就是這樣漸漸有了...
    小北北媽閱讀 801評論 3 7