28 - JavaScript 特效:scroll家族和緩動動畫

緩動動畫

三個函數

緩慢動畫裏,我們要用到三個函數,這裏先列出來:

  • Math.ceil() 向上取整

  • Math.floor() 向下取整

  • Math.round(); 四捨五入

緩動動畫的原理

緩動動畫的原理就是:在移動的過程中,步長越來越小。

設置步長爲**:目標位置和盒子當前位置的十分之一**。用公式表達,即:

	盒子位置 = 盒子本身位置 + (目標位置 - 盒子本身位置)/ 10;

代碼舉例:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
            position: absolute;
        }
    </style>
</head>
<body>
<button>運動到left = 400px</button>
<div></div>

<script>

    var btn = document.getElementsByTagName("button")[0];
    var div = document.getElementsByTagName("div")[0];

    btn.onclick = function () {
        setInterval(function () {
            //動畫原理:盒子未來的位置 = 盒子當前的位置+步長
            div.style.left = div.offsetLeft + (400 - div.offsetLeft) / 10 + "px";
        }, 30);
    }

</script>
</body>
</html>

效果:
在這裏插入圖片描述

緩慢動畫的封裝(解決四捨五入的問題)

我們發現一個問題,上圖中的盒子最終並沒有到達400px的位置,而是隻到了396.04px就停住了:

在這裏插入圖片描述

原因是:JS在取整的運算時,進行了四捨五入。

我們把打印396.04px這個left值打印出來看看:
在這裏插入圖片描述

我麼發現,通過div.style.left獲取的值是精確的,通過div.offsetLeft獲取的left值會進行四捨五入。

此時,我們就要用到取整的函數了。

通過對緩動動畫進行封裝,完整版的代碼實現如下:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
            position: absolute;
            left: 0;
        }
    </style>
</head>
<body>
<button>運動到200</button>
<button>運動到400</button>
<div></div>

<script>

    var btn = document.getElementsByTagName("button");
    var div = document.getElementsByTagName("div")[0];

    btn[0].onclick = function () {
        animate(div, 200);
    }

    btn[1].onclick = function () {
        animate(div, 400);
    }

    //緩動動畫封裝
    function animate(ele, target) {
        //要用定時器,先清定時器
        //一個蘿蔔一個坑兒,一個元素對應一個定時器
        clearInterval(ele.timer);
        //定義定時器
        ele.timer = setInterval(function () {
            //獲取步長
            //步長應該是越來越小的,緩動的算法。
            var step = (target - ele.offsetLeft) / 10;
            //對步長進行二次加工(大於0向上取整,小於0向下取整)
            //達到的效果是:最後10像素的時候都是1像素1像素的向目標位置移動,就能夠到達指定位置。
            step = step > 0 ? Math.ceil(step) : Math.floor(step);
            //動畫原理: 目標位置 = 當前位置 + 步長
            ele.style.left = ele.offsetLeft + step + "px";
            console.log(step);
            //檢測緩動動畫有沒有停止
            console.log("smyhvae");
            if (Math.abs(target - ele.offsetLeft) <= Math.abs(step)) {
                //處理小數賦值
                ele.style.left = target + "px";
                clearInterval(ele.timer);
            }
        }, 30);
    }

</script>
</body>
</html>

實現效果:
在這裏插入圖片描述

scroll 家族的組成

當我們用鼠標滾輪,滾動網頁的時候,會觸發window.onscroll()方法。效果如下:(注意看控制檯的打印結果)

在這裏插入圖片描述

1、ScrollWidth 和 scrollHeight

獲取盒子的寬高。調用者爲節點元素。不包括 border和margin。如下:

  • scrollWidth = width + padding;

  • scrollHeight = height + padding;

scrollHeight有一個特點:如果文字超出了盒子,高度爲內容的高(包括超出的內容);不超出,則是盒子本身高度。

舉例:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        div {
            width: 100px;
            height: 100px;
            padding: 10px;
            margin: 3px;
            border: 8px solid red;
        }
    </style>
</head>
<body>

<div class="box">
    靜,能寒窗苦守;動,能點石成金。
    靜,能寒窗苦守;動,能點石成金。
    靜,能寒窗苦守;動,能點石成金。
    靜,能寒窗苦守;動,能點石成金。
    靜,能寒窗苦守;動,能點石成金。
    靜,能寒窗苦守;動,能點石成金。
</div>
<script>

    var div = document.getElementsByTagName("div")[0];

    //scrollHeight有一個特點:如果文字超出了盒子,高度爲內容的高(包括超出的內容);不超出,則是盒子本身高度。
    //IE8以下(不包括IE8),爲盒子本身內容的多少。
    console.log(div.scrollWidth);
    console.log(div.scrollHeight);

</script>
</body>
</html>

打印結果:

在這裏插入圖片描述

2、scrollTop 和 scrollLeft

網頁被捲去的頭部和左邊的部分。

比如說,一個網頁往上滾動的時候,上面的部分自然被瀏覽器遮擋了,遮擋的高度就是scrollTop。

scrollTop 這個屬性的寫法要注意兼容性,如下。

(1)如果文檔沒有 DTD 聲明,寫法爲:

    document.body.scrollTop

在沒有 DTD 聲明的情況下,如果不是這種寫法,chrome瀏覽器認不出來。

(2)如果文檔有 DTD 聲明,寫法爲:

   document.documentElement.scrollTop

在有 DTD 聲明的情況下,如果不是這種寫法,IE678認不出來。

綜合上面這兩個,就誕生了一種兼容性的寫法:

    document.body.scrollTop || document.documentElement.scrollTop //方式一

    document.body.scrollTop + document.documentElement.scrollTop  //方式二

另外還有一種兼容性的寫法:window.pageYOffsetwindow.pageXOffset。這種寫法無視DTD的聲明。這種寫法支持的瀏覽器版本是:火狐/谷歌/ie9+。

綜合上面的幾種寫法,爲了兼容,不管有沒有DTD,最終版的兼容性寫法:

    window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;

判斷是否已經 DTD聲明

方法如下:

    document.compatMode === "CSS1Compat"   // 已聲明
    document.compatMode === "BackCompat"   // 未聲明

將 scrollTop 和 scrollLeft封裝爲 json

將 scrollTop 和 scrollLeft封裝爲一個方法,名叫scroll(),返回值爲 json。json裏的鍵爲 top 和 left。以後就直接調用json.top 和json.left就好。

代碼實現:

<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        body {
            height: 5000px;
            width: 5000px;
        }
    </style>
</head>
<body>

<script>

    //需求:封裝一個兼容的scroll().返回值是json,用scroll().top獲取scrollTop,用scroll().left獲取scrollLeft

    window.onscroll = function () {
//        var json = scroll();
//        json.top;
        console.log(scroll().top);
        console.log(scroll().left);
    }

    //函數封裝(簡單封裝,實際工作使用)
    function scroll() {
        return { //此函數的返回值是json
            "top": window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop,
            "left": window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft
        }
    }
</script>
</body>
</html>

上方代碼中,函數定義的那部分就是要封裝的代碼。另外還有一種較爲複雜的封裝方式:

function scroll() {  // 開始封裝自己的scrollTop
    if(window.pageYOffset !== undefined) {  // ie9+ 高版本瀏覽器
        // 因爲 window.pageYOffset 默認的是  0  所以這裏需要判斷
        return {
            left: window.pageXOffset,
            top: window.pageYOffset
        }
    }
    else if(document.compatMode === "CSS1Compat") {    // 標準瀏覽器   來判斷有沒有聲明DTD
        return {
            left: document.documentElement.scrollLeft,
            top: document.documentElement.scrollTop
        }
    }
    return {   // 未聲明 DTD
        left: document.body.scrollLeft,
        top: document.body.scrollTop
    }
}

獲取 html 文檔的方法

獲取title、body、head、html標籤的方法如下:

  • document.title 文檔標題;

  • document.head 文檔的頭標籤

  • document.body 文檔的body標籤;

  • document.documentElement (這個很重要)。

document.documentElement表示文檔的html標籤。也就是說,基本結構當中的 html 標籤而是通過document.documentElement訪問的,並不是通過 document.html 去訪問的。

scrollTop 舉例:固定導航欄

完整版代碼實現:

(1)index.html:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        * {
            margin: 0;
            padding: 0
        }

        img {
            vertical-align: top;
        }

        .main {
            margin: 0 auto;
            width: 1000px;
            margin-top: 10px;

        }

        #Q-nav1 {
            overflow: hidden;
        }

        .fixed {
            position: fixed;
            top: 0;
            left: 0;
        }
    </style>

    <!--引入工具js-->
    <script src="tools.js"></script>
    <script>
        window.onload = function () {
            //需求1:當我們滾動界面的時候,被捲曲的頭部如果超過第二個盒子距離頂部的位置,那麼直接給第二個盒子加類名.fixed
            //需求2:當我們滾動界面的時候,被捲曲的頭部如果小於第二個盒子距離頂部的位置,那麼直接給第二個盒子取消類名.fixed

            //1.老三步。
            var topDiv = document.getElementById("top");
            var height = topDiv.offsetHeight;
            var middle = document.getElementById("Q-nav1");
            var main = document.getElementById("main");

            window.onscroll = function () {
                //2.判斷 ,被捲曲的頭部的大小
                if (scroll().top > height) {
                    //3.滿足條件添加類,否則刪除類
                    middle.className += " fixed";
                    //第二個盒子也要佔位置,爲了避免重疊,我們給第三個盒子一個上padding的空間,把這個空間留給第二個盒子
                    main.style.paddingTop = middle.offsetHeight + "px";
                } else {
                    middle.className = "";
                    //清零
                    main.style.paddingTop = 0;
                }
            }

        }
    </script>
</head>
<body>
<div class="top" id="top">
    <img src="images/top.png" alt=""/>
</div>
<div id="Q-nav1">
    <img src="images/nav.png" alt=""/>
</div>
<div class="main" id="main">
    <img src="images/main.png" alt=""/>
</div>
</body>
</html>

上方代碼中,有一個技巧:

main.style.paddingTop = middle.offsetHeight + "px";

仔細看註釋就好。

(2)tools.js:

/**
 * Created by smyhvae on 2018/02/03.
 */
function scroll() {  // 開始封裝自己的scrollTop
    if (window.pageYOffset !== undefined) {  // ie9+ 高版本瀏覽器
        // 因爲 window.pageYOffset 默認的是  0  所以這裏需要判斷
        return {
            left: window.pageXOffset,
            top: window.pageYOffset
        }
    }
    else if (document.compatMode === "CSS1Compat") {    // 標準瀏覽器   來判斷有沒有聲明DTD
        return {
            left: document.documentElement.scrollLeft,
            top: document.documentElement.scrollTop
        }
    }
    return {   // 未聲明 DTD
        left: document.body.scrollLeft,
        top: document.body.scrollTop
    }
}

實現效果:
在這裏插入圖片描述

window.scrollTo()方法舉例:返回到頂部小火箭

(1)index.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>river</title>
    <style>
        img {
            position: fixed;
            bottom: 100px;
            right: 50px;
            cursor: pointer;
            display: none;
            border: 1px solid #000;
        }

        div {
            width: 1210px;
            margin: 100px auto;
            text-align: center;
            font: 500 26px/35px "simsun";
            color: red;
        }
    </style>
    <script src="tools.js"></script>
    <script>
        window.onload = function () {
            //需求:被捲去的頭部超過100顯示小火箭,然後點擊小火箭屏幕緩慢移動到最頂端。
            //難點:我們以前是移動盒子,現在是移動屏幕,我們沒有學過如何移動屏幕。
            //      技術點:window.scrollTo(x,y);瀏覽器顯示區域跳轉到指定的座標
            //步驟:
            //1.老三步
            var img = document.getElementsByTagName("img")[0];
            window.onscroll = function () {
                //被捲去的距離大於200顯示小火箭,否則隱藏
                //2.顯示隱藏小火箭
                if (scroll().top > 1000) {
                    img.style.display = "block";
                } else {
                    img.style.display = "none";
                }
                //每次移動滾動條的時候都給leader賦值,模擬leader獲取距離頂部的距離
                leader = scroll().top;
            }
            //3.緩動跳轉到頁面最頂端(利用我們的緩動動畫)
            var timer = null;
            var target = 0; //目標位置
            var leader = 0; //顯示區域自身的位置
            img.onclick = function () {
                //技術點:window.scrollTo(0,0);
                //要用定時器,先清定時器
                clearInterval(timer);
                timer = setInterval(function () {
                    //獲取步長
                    var step = (target - leader) / 10;
                    //二次處理步長
                    step = step > 0 ? Math.ceil(step) : Math.floor(step);
                    leader = leader + step; //往上移動的過程中,step是負數。當前位置減去步長,就等於下一步的位置。
                    //顯示區域移動
                    window.scrollTo(0, leader);
                    //清除定時器
                    if (leader === 0) {
                        clearInterval(timer);
                    }
                }, 25);
            }
        }
    </script>
</head>

<body>
    <img src="images/Top.jpg" />
    <div>
        我是最頂端.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
        我命由我不由天.....<br>
    </div>
</body>

</html>

(2)tools.js:

//函數:獲取scrollTop和scrollLeft的值
function scroll() {  // 開始封裝自己的scrollTop
    if (window.pageYOffset != null) {  // ie9+ 高版本瀏覽器
        // 因爲 window.pageYOffset 默認的是  0  所以這裏需要判斷
        return {
            left: window.pageXOffset,
            top: window.pageYOffset
        }
    }
    else if (document.compatMode === "CSS1Compat") {    // 標準瀏覽器   來判斷有沒有聲明DTD
        return {
            left: document.documentElement.scrollLeft,
            top: document.documentElement.scrollTop
        }
    }
    return {   // 未聲明 DTD
        left: document.body.scrollLeft,
        top: document.body.scrollTop
    }
}

小火箭的圖片資源:
在這裏插入圖片描述

window.scrollTo()方法舉例:樓層跳躍(暫略)

緩動框架封裝

1、緩動框架封裝:同時設置多個屬性

這裏我們通過window.getComputedStyle的方式獲取屬性值。

完整代碼如下:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        div {
            position: absolute;
            top: 40px;
            left: 10px;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>
<body>

<button>運動到json中設置的位置</button>
<div></div>

<script>

    var btnArr = document.getElementsByTagName("button");
    var div = document.getElementsByTagName("div")[0];

    btnArr[0].onclick = function () {
        var json = {"left": 100, "top": 200, "width": 300, "height": 300};
        animate(div, json);
    }

    //參數變爲3個
    function animate(ele, json) {
        //先清定時器
        clearInterval(ele.timer);
        ele.timer = setInterval(function () {
            //遍歷屬性和值,分別單獨處理json
            //attr == key(鍵)    target == json[key](值)
            for (var key in json) {
                //四部
                var current = parseInt(getStyle(ele, key)) || 0;
                //1.獲取步長
                var step = (json[key] - current) / 10;
                //2.二次加工步長
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                current = current + step;
                //3.賦值
                ele.style[key] = current + "px";
                console.log(1);
                //4.清除定時器
//                    if(Math.abs(json[key]-current)<=Math.abs(step)){
//                        ele.style[key] = json[key] + "px";
//                        clearInterval(ele.timer);
//                    }
            }
        }, 25);
    }

    //兼容方法獲取元素樣式
    function getStyle(ele, attr) {
        if (window.getComputedStyle) {
            return window.getComputedStyle(ele, null)[attr];
        }
        return ele.currentStyle[attr];
    }
</script>
</body>
</html>

實現效果:
在這裏插入圖片描述

2、上方的代碼改進:清除定時器

上方的代碼中,我們還需做一下清除定時器的判斷:只有所有的參數都到達指定位置了,我們就清除定時器。

完整版代碼如下:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        div {
            position: absolute;
            top: 40px;
            left: 10px;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>
<body>

<button>運動到json中設置的位置</button>
<div></div>

<script>

    var btnArr = document.getElementsByTagName("button");
    var div = document.getElementsByTagName("div")[0];

    btnArr[0].onclick = function () {
        var json = {"left": 100, "top": 200, "width": 300, "height": 300};
        animate(div, json);
    }

    //參數變爲3個
    function animate(ele, json) {
        //先清定時器
        clearInterval(ele.timer);
        ele.timer = setInterval(function () {
            //開閉原則
            var bool = true;

            //遍歷屬性和值,分別單獨處理json
            //attr == key(鍵)    target == json[key](值)
            for (var key in json) {
                //四部
                var current = parseInt(getStyle(ele, key)) || 0;
                //1.獲取步長
                var step = (json[key] - current) / 10;
                //2.二次加工步長
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                current = current + step;
                //3.賦值
                ele.style[key] = current + "px";
                //4.清除定時器
                //判斷: 目標值和當前值的差大於步長,就不能跳出循環
                //不考慮小數的情況:目標位置和當前位置不相等,就不能清除清除定時器。
                if (json[key] !== current) {
                    bool = false;
                }
            }

            console.log(1);
            //只有所有的屬性都到了指定位置,bool值才爲true;
            if (bool) {
                clearInterval(ele.timer);
            }
        }, 25);
    }

    //兼容方法獲取元素樣式
    function getStyle(ele, attr) {
        if (window.getComputedStyle) {
            return window.getComputedStyle(ele, null)[attr];
        }
        return ele.currentStyle[attr];
    }

</script>
</body>
</html>

運行效果同上。

### 3、進一步深入:如果有要同時執行謳多個動畫時,就要用到回調函數(重要)

上面的代碼中,我們要做的動畫是:

    btnArr[0].onclick = function () {
        var json = {"left": 100, "top": 200, "width": 300, "height": 300};
        animate(div, json);
    }

上面的代碼是執行這一個動畫,可如果要同時執行兩個動畫呢?一般人自然想到的是下面的寫法:(錯誤的寫法)

    btnArr[0].onclick = function () {
        var json1 = {"left": 100, "top": 200, "width": 300, "height": 300};
        var json2 = {"left": 200, "top": 10, "width": 150, "height": 150};
        animate(div, json1);
        animate(div, json2);
    }

但是這樣寫的話,第二個動畫 json2 會把第一個動畫 json1 層疊掉。也就是說,第一個動畫直接就不執行了。效果如下:
在這裏插入圖片描述

這顯然不是我們想看到的。

如果我們想先執行第一個動畫fn1(),再執行第二個動畫fn2()的話,就要用到回調函數。意思是,將第二個動畫fn2()作爲回調函數即可。

完整版代碼如下:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        div {
            position: absolute;
            top: 40px;
            left: 10px;
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>
<body>

<button>運動到 json 設置的位置</button>
<div></div>

<script>

    var btnArr = document.getElementsByTagName("button");
    var div = document.getElementsByTagName("div")[0];

    btnArr[0].onclick = function () {
        var json1 = {"left": 100, "top": 200, "width": 300, "height": 300};
        var json2 = {"left": 300, "top": 10, "width": 100, "height": 100};

        animate(div, json1, function () { //第三個參數是回調,可以保證json1的動畫執行結束後,再執行json2的動畫
            animate(div, json2);
        })
    }

    //帶有回調的動畫封裝
    function animate(ele, json, fn) {
        //先清定時器
        clearInterval(ele.timer);
        ele.timer = setInterval(function () {
            //開閉原則
            var bool = true;


            //遍歷屬性和值,分別單獨處理json
            //attr == key(鍵)    target == json[key](值)
            for (var key in json) {
                //四部
                var current = parseInt(getStyle(ele, key)) || 0;
                //1.獲取步長
                var step = (json[key] - current) / 10;
                //2.二次加工步長
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                current = current + step;
                //3.賦值
                ele.style[key] = current + "px";
                //4.清除定時器
                //判斷: 目標值和當前值的差大於步長,就不能跳出循環
                //不考慮小數的情況:目標位置和當前位置不相等,就不能清除清除定時器。
                if (json[key] !== current) {
                    bool = false;
                }
            }

            console.log(1);
            //只有所有的屬性都到了指定位置,bool值纔不會變成false;
            if (bool) {
                clearInterval(ele.timer); //定時器結束,代表第一個函數fn1()執行完畢了,接下來可以執行回調函數fn2()了。
                //只有傳遞了回調函數,才能執行
                if (fn) {  //【重要】第一個函數執行完畢了,定時器也清除了。現在,如果有人送了fn()這個回調函數過來,那就執行fn()
                    fn();  // 函數名+():執行該函數
                }
            }
        }, 25);
    }

    //兼容方法獲取元素樣式
    function getStyle(ele, attr) {
        if (window.getComputedStyle) {
            return window.getComputedStyle(ele, null)[attr];
        }
        return ele.currentStyle[attr];
    }

</script>
</body>
</html>

效果如下:
在這裏插入圖片描述

上方代碼中,如果我們要先後完成兩個動畫,執行的代碼是:

        animate(div, json1, function () { //第三個參數是回調,可以保證json1的動畫執行結束後,再執行json2的動畫
            animate(div, json2);
        })

如果想要先後執行兩個動畫,那就以此類推:

        animate(div, json1, function () { //第三個參數是回調,可以保證json1的動畫執行結束後,再執行json2的動畫
            animate(div, json2,function () {
                animate(div,json3);
            });
        })

舉例:仿360的右下角開機效果

代碼實現:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        .box {
            width: 322px;
            position: fixed;
            bottom: 0;
            right: 0;
        }

        span {
            position: absolute;
            top: 0;
            right: 0;
            width: 30px;
            height: 20px;
            cursor: pointer;
        }
    </style>
    <script>
        window.onload = function () {
            //需求:下面的盒子高先變爲0,然後上面的大盒子的寬再變爲0.
            var guanbi = document.getElementById("guanbi");
            var box = guanbi.parentNode;
            var b = document.getElementById("b");

            guanbi.onclick = function () {
                //下面的盒子高度變爲0,然後大盒子的寬在變爲0.
                animate(b, {"height": 0}, function () {
                    animate(box, {"width": 0});
                });
            }
        }


        //封裝好的動畫函數
        function animate(ele, json, fn) {
            //先清定時器
            clearInterval(ele.timer);
            ele.timer = setInterval(function () {
                //開閉原則
                var bool = true;


                //遍歷屬性和值,分別單獨處理json
                //attr == key(鍵)    target == json[key](值)
                for (var key in json) {
                    //四部
                    var current = parseInt(getStyle(ele, key)) || 0;
                    //1.獲取步長
                    var step = (json[key] - current) / 10;
                    //2.二次加工步長
                    step = step > 0 ? Math.ceil(step) : Math.floor(step);
                    current = current + step;
                    //3.賦值
                    ele.style[key] = current + "px";
                    //4.清除定時器
                    //判斷: 目標值和當前值的差大於步長,就不能跳出循環
                    //不考慮小數的情況:目標位置和當前位置不相等,就不能清除清除定時器。
                    if (json[key] !== current) {
                        bool = false;
                    }
                }

                console.log(1);
                //只有所有的屬性都到了指定位置,bool值纔不會變成false;
                if (bool) {
                    clearInterval(ele.timer);
                    //所有程序執行完畢了,現在可以執行回調函數了
                    //只有傳遞了回調函數,才能執行
                    if (fn) {
                        fn();
                    }
                }
            }, 1);
        }

        /**
         * 獲取元素樣式兼容寫法
         * @param ele
         * @param attr
         * @returns {*}
         */
        function getStyle(ele, attr) {
            if (window.getComputedStyle) {
                return window.getComputedStyle(ele, null)[attr];
            }
            return ele.currentStyle[attr];
        }

    </script>
</head>
<body>
<div class="box">
    <span id="guanbi"></span>
    <div class="hd" id="t">
        <img src="images/1.jpg" alt=""/>
    </div>
    <div class="bd" id="b">
        <img src="images/2.jpg" alt=""/>
    </div>
</div>
</body>
</html>

效果如下:
在這裏插入圖片描述

4、對 opacity和 z-index 屬性進行單獨改進

我們以上的代碼中,如果要進行動畫參數的設置,是直接把參數放到json裏面去的。例如:

        var json1 = {"left": 100, "top": 200, "width": 300, "height": 300};
        var json2 = {"left": 300, "top": 10, "width": 100, "height": 100};

可是,下面這兩個屬性,卻不能這樣放到json裏,會出現兼容性的問題:

    opacity: 0.5;  //透明度
    z-index: 1;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章