JavaScript 進階知識 - 特效篇(一)

image

JS特效

前言

通過前面幾篇文章的講解,相信你們已經會操做DOMBOM了。爲何前面要花那麼多精力去講DOM呢?由於在後面的學習、工做中,會大量的使用DOM操做,一個表格須要增、刪、改、查,一個圖片須要改變大小..等,若是你想要動態的改變這些,必需要學會使用DOMjavascript

爲了鞏固前面的知識點,而且可以熟練地使用它們,這裏單獨寫了一篇《JavaScript 進階知識 - 特效篇》。本篇文章做爲進階篇很重要,不僅僅是對前面知識點的運用,期間也會有大量的新知識點注入,因此但願小夥伴們繼續加油,認真閱讀。css

在本篇文章中主要會講解一些案例,好比咱們平時在頁面中碰到的一些特效,一些動畫效果。html

注意: 全部的案例都在這裏連接: 提取密碼密碼: 70ny,文章中的每一個案例後面都有對應的序號。java

1. offset 系列

offset系列用於用於獲取元素自身的大小和位置,在網頁特效中有普遍應用。 offset系列主要有: offsetHeightoffsetWidthoffsetParentoffsetLeftoffsetTop

1.1 offsetWidth 和 offsetHeight

offsetWidthoffsetHeight獲取的是元素的真實寬高
  • 獲取的是元素真實的高度和寬度
  • 獲取到的是數值類型,方便計算
  • offsetHeightoffsetWidth是隻讀屬性,不能設置。

示例代碼:獲取一個盒子的真實寬高 [01-offset系列-offsetWidth&Height.html]面試

<!-- 樣式部分 -->
<style>
    div {
        width: 200px;
        height: 100px;
        background-color: pink;
        padding: 10px;
        border: 10px solid salmon;
        margin: 10px;
    }
</style>

<!-- html 部分 -->
<div id="box"></div>

<!-- js 部分 -->
<script>
    var box = document.getElementById('box');
    // offsetWidth是一個經過計算後獲得的值, padding + border + width
    console.log(box.offsetWidth);   // 240
    console.log(box.offsetHeight);  // 140
</script>

offsetWidth是一個經過計算後獲得的值, padding + border + widthsegmentfault

思考: 以前咱們不是也能夠經過style來獲取樣式嗎?他們有什麼不一樣數組

style.heightstyle.width只能獲取到行內樣式裏的 widthheight
  • 獲取的是字符串類型,還須要轉換成數值類型
  • 寫在css樣式裏的寬高是獲取不到的,只能獲取行內樣式

總結:瀏覽器

  • 設置寬度高度使用style.widthstyle.height
  • 獲取寬度和高度offsetWidthoffsetHeight
  • offset獲取的寬高包括paddingborder

1.2 offsetParent

parentNodeoffsetParent
  • parentNode始終是父元素
  • offsetParent是離當前元素最近的定位元素(absoluterelative),若是沒有,那就找body

示例代碼: [02-offset系列-offsetParent.html]app

<!-- 樣式部分 -->
<style>
    #father {
        width: 500px;
        height: 500px;
        background-color: #FF9F68;
    }
    
    #son {
        width: 300px;
        height: 300px;
        background-color: #FEFF89;
    }
    
    #grandson {
        width: 100px;
        height: 100px;
        background-color: #AC005D;
        position: absolute;
        left: 100px;
        right: 100px;
    }
</style>

<!-- html 部分 -->
<div id="father">
    <div id="son">
        <div id="grandson"></div>
    </div>
</div>

<!-- js 部分 -->
<script>
    var grandSon = document.getElementById("grandson");
    // 找父節點  親爹
    console.log(grandSon.parentNode);           // 返回<div id="son"></div>
    // 找最近有定位的爹,若是找不到,會找body
    console.log(grandSon.offsetParent);         // 返回<body></body>
</script>

1.3 offsetLeft與offsetTop

offsetLeft: 自身左側到 offsetParent左側的距離: left + margin

offsetTop: 自身頂部到offsetParent頂部的距離 : top + margin框架

  • 元素自身與offsetParent真實的距離
  • 獲取到的是數值類型,方便計算
  • 只讀屬性,只能獲取,不能設置

示例代碼:獲取一個盒子距父盒子的距離 [03-offset系列-offsetTop&Left.html]

<!-- 樣式部分 -->
<style>
    #father {
        width: 400px;
        height: 400px;
        background-color: pink;
        position: relative;
        margin-left: 100px;
    }
    
    #son {
        width: 200px;
        height: 200px;
        background-color: skyblue;
        position: absolute;
        left: 100px;
        margin: 20px;
    }
</style>

<!-- html 部分 -->
<div id="father">
    <div id="son"></div>
</div>

<!-- js 部分 -->
<script>
    //offsetLeft與offsetTop
    var son = document.getElementById("son");
    console.log(son.offsetLeft);    // 120
    console.log(son.offsetTop);     // 20
</script>

思考: 以前咱們不是也能夠經過style來獲取樣式嗎?他們有什麼不一樣

style.topstyle.left只能獲取到行內樣式裏的 topleft
  • 獲取的是字符串類型,還須要轉換成數值類型
  • 寫在css樣式裏的寬高是獲取不到的,只能獲取行內樣式

總結:

  • 設置定位left/top使用style.leftstyle.top
  • 獲取定位left/top使用offsetLeftoffsetTop
  • offset獲取的位置包括margin
  • 若是父元素沒有定位,獲取的就是相對body

一張圖看清offset系列

image

2. 勻速動畫框架

2.1 勻速動畫初體驗

如何讓一個物體動起來?動畫函數的實現原理其實就是利用間歇定時器每隔一段時間執行一次的原理實現的。

一、讓一個物體動起來

點擊按鈕讓一個盒子勻速往右執行一段距離:[04-勻速動畫初體驗(一).html]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
    }
    
    #box {
        width: 100px;
        height: 100px;
        background-color: hotpink;
        position: absolute;
    }
</style>

<!-- html 部分 -->
<button id="btn">奔跑吧</button>
<div id="box"></div>

<!-- js 部分 -->
<script>
    var btn = document.getElementById('btn');
    var box = document.getElementById('box');

    btn.onclick = function() {
        setInterval(function() {
            // 定義一個距離 至關於每一次要跑的距離 step
            var step = 5;
            // 定義一個當前位置 leader
            var leader = box.offsetLeft;
            // 每次執行的時候 讓leader都走step距離
            leader = leader + step;
            // 將距離賦值給box
            box.style.left = leader + "px";
        // 每15ms 就執行一次 人眼視覺停留 就有動畫效果了
        }, 15);
    }
</script>

效果圖:

image

BUG: 不知道細心的小夥伴有沒有發現兩個問題

  • 如今執行的時候是不會停下來的,一直往右跑
  • 點擊按鈕以後再去點擊,會發現,按鈕點擊次數越多,盒子速度越快

二、讓一個物體動起來,解決bug

咱們讓盒子運動到500px的位置停下來 [05-勻速動畫初體驗(二).html]

var btn = document.getElementById('btn');
var box = document.getElementById('box');
var timer = null;
/**
    爲何會越點越快?
    點擊一次就會調用一次定時器,點擊的次數越多,調用的就越多
    距離疊加的就會愈來愈大 視覺效果上看起來就跑的愈來愈快
    只要在每次點擊後,定時器執行前清除上一次定時器,就不會出現越點越快的效果了
*/
btn.onclick = function() {
    // 一進來就清除定時器
    clearInterval(timer);
    timer = setInterval(function() {
        // 定義一個距離 至關於每一次要跑的距離 step
        var step = 5;
        // 定義一個當前位置 leader
        var leader = box.offsetLeft;
        /**
            當移動的位置在500px內的時候,執行動畫函數
            不然就清除定時器,讓盒子停下來
        */
        if (leader < 500) {
            // 每次執行的時候 讓leader都走step距離
            leader = leader + step;
            // 將距離賦值給box
            box.style.left = leader + "px";
        } else {
            clearInterval(timer);
        }
    }, 15);
}

效果圖:

image

總結:

  • setInterval間歇定時器,若是不手動清除,它就會一直運行下去
  • 點擊事件觸發定時器必定要注意,一進來就清除一次,不然會越點越快

2.2 勻速動畫函數封裝

函數須要獨立,就不能使用全局變量。 timer以前是一個全局變量,若是不獨立,頁面只有一個定時器在運做。封裝的函數裏將 timer綁定給調用定時器的元素,這樣就獨立了。

一、封裝一個動畫函數 [06-封裝一個勻速動畫函數.html]

<!-- html 部分 -->
<button id="btn">奔跑吧,500</button>
<button id="btn2">奔跑吧,1000</button>
<div id="box"></div>

<!-- js 部分 -->
<script>
    var btn = document.getElementById('btn');
    var btn2 = document.getElementById('btn2');
    var box = document.getElementById('box');

    /**
        既然是封裝的函數,有些不肯定的,常常變的元素就要提出來
        好比:  1.每一次改變的距離 num
               2.調用動畫的對象 box ==> element  
               3.運動的目標距離 500 ==> target
    */
    // 封裝一個動畫函數
    function animate(element, target,num) {
        // 一進來就清除定時器
        // 函數須要獨立,就不能使用全局變量 因此將timer綁定在element上
        clearInterval(element.timer);
        element.timer = setInterval(function() {
            // 定義一個距離 至關於每一次要跑的距離 step
            var step = num;
            // 定義一個當前位置 leader
            var leader = element.offsetLeft;
            if (leader < target) {
                // 每次執行的時候 讓leader都走step距離
                leader = leader + step;
                // 將距離賦值給box
                element.style.left = leader + "px";
            } else {
                clearInterval(element.timer);
            }
        }, 15);

    }
    // 點擊按鈕1 移動到500px的位置
    btn.onclick = function() {
        animate(box, 500, 9);
    }

    // 點擊按鈕2 移動到1000px的位置
    btn2.onclick = function() {
        animate(box, 1000, 5);
    }
</script>

注意: 上面的案例咱們只是簡單的實現了一個動畫的封裝效果,可是做爲一個之後會常常用的函數,上面的代碼還有不少須要優化的地方

  • 一、上面的函數只能往正方向跑,也就是說去到1000,想讓它回到500是很差實現的;
  • 二、若是每次走的距離是5,目標距離是500,正好能整除。假如每次走的是9呢?每次走9,是不能被500整除的,因此最後停下里的距離會偏多一點。image

二、封裝一個動畫函數完整版 [07-封裝一個勻速動畫函數完整版.html]

  • 先說說第二個問題,距離的問題。若是走的距離不能被目標距離整除的話,最後會多出來一點距離,咱們能夠不用管這個距離,直接在清除定時器,停下里的時候讓它的距離等於目標距離。
clearInterval(element.timer);        // 清除前位置在504,直接在下面設置讓它位置等於500
element.style.left = target + "px";  // 500
  • 如今說說第一個問題,盒子到1000的時候不能回到500。假設如今盒子在1000,咱們點擊按鈕1的時候想要讓他回到500,這個時候咱們能夠發現時的leader = 1000,目標距離target爲500,就是說當leader>target的時候,盒子是能夠往回走的,這時候只要將步數設置爲負數 ,盒子就是往回跑的。
var leader = element.offsetLeft;
// 當目標距離大於當前位置 說明往正方向走 step的值就是正的
var step = target > leader? 9 : -9;
  • 此時就不能再根據 if (leader < target){}, else { clearInterval(element.timer); }去判斷,讓盒子運動了。這時的判斷條件應該是目標距離target 與盒子目前距離leader之間差的絕對值大於等於一步距離step絕對值的時候,讓他們執行leader = leader + step;不然的話清除清除定時器,並將最後的距離直接設置爲target的距離。
var distance = Math.abs(target - leader);
// 經過判斷此時的差若是大於或者等於一步的距離step的時候,就應該執行動畫
if (distance >= Math.abs(step)) {
    leader = leader + step;
    element.style.left = leader + "px";
}

完整代碼:

<!-- html 部分 -->
<button id="btn">奔跑吧,500</button>
<button id="btn2">奔跑吧,1000</button>
<div id="box"></div>

<!-- js 部分 -->
<script>
    var btn = document.getElementById('btn');
    var btn2 = document.getElementById('btn2');
    var box = document.getElementById('box');

    function animate(element, target, num) {
        clearInterval(element.timer);
        element.timer = setInterval(function() {
            var leader = element.offsetLeft;
            // 判斷此時每次走的距離,當目標距離大於當前位置 說明往正方向走 step的值就是正的
            var step = target > leader ? num : -num;
            // 得到此時的距離 與目標距離的差的絕對值
            var distance = Math.abs(target - leader);
            // 經過判斷此時的差若是大於或者等於一步的距離step的時候,就應該執行動畫
            if (distance >= Math.abs(step)) {
                leader = leader + step;
                element.style.left = leader + "px";
            } else {
                // 不然清除動畫,而且將最後的距離設置爲target的距離
                clearInterval(element.timer);
                element.style.left = target + "px";
            }
        }, 15);

    }
    btn.onclick = function() {
        animate(box, 500, 9);
    }

    btn2.onclick = function() {
        animate(box, 1000, 5);
    }
</script>

效果圖:

image

如上,這就是封裝的一個完美的動畫函數了,下次有須要用到動畫的地方,直接引用便可——[ js/animate.js ]

3. 輪播圖

基本上每一個網站都會用到輪播圖,輪播圖的使用能夠說是必不可少的。之後咱們用的最多的多是插件,原生的可能並不經常使用,可是輪播圖的原理咱們必須知道,而且可以寫出來。(以前一次面試就是讓我講出輪播圖的具體實現步驟)

3.1 簡單輪播圖

如今咱們先來學習下簡單的輪播圖實現原理。

輪播圖樣式的特色:

  • ul要足夠的寬,要求可以一行放下全部的li
  • 父盒子的寬高和圖片的寬高同樣
  • 父盒子要有一個overflow:hidden ,僅顯示一張圖片,很少很多

要求ul很寬很寬,由於全部的li要左浮動,要保證全部的li在一行上顯示,定義一個盒子,盒子的寬高要和顯示的單張圖片寬高同樣,而後設置overflow:hidden這樣其餘的li就會被隱藏在下面,經過改變ul的位置就能實現圖片的切換了

示例代碼: [08-實現簡單的輪播圖.html]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
        list-style: none;
    }
    #slide {
        width: 560px;
        height: 315px;
        margin: 100px auto;
        position: relative;
        overflow: hidden;
    }
    #slide ul {
        width: 600%;
        position: absolute;
    }
    #slide ul li {
        float: left;
    }
    #slide ul img {
        display: block;
    }
    #slide ol {
        width: 100px;
        height: 14px;
        background-color: rgba(255, 255, 255, .6);
        /* background-color: pink; */
        position: absolute;
        bottom: 14px;
        left: 50%;
        margin-left: -50px;
        border-radius: 7px;
    }
    #slide ol li {
        width: 10px;
        height: 10px;
        float: left;
        background-color: #fff;
        border-radius: 50%;
        margin-top: 2px;
        margin-left: 8.5px;
        cursor: pointer;
    }
    #slide ol li.current {
        background-color: #DF654A;
    }
</style>

<!-- html 部分 -->
<div id="slide">
    <ul>
        <li>
            <a href="#"><img src="../image/1.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/2.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/3.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/4.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/5.jpg" alt=""></a>
        </li>
    </ul>

    <ol>
        <li class="current"></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
    </ol>
</div>

<!-- js 部分 -->
<script src="../js/animate.js"></script>
<script>
    var slide = document.getElementById('slide');
    var ul = slide.children[0];
    var ol = slide.children[1];
    // ol 下的 li 小圓點
    var lis = ol.children;
    var imgWidth = slide.offsetWidth;
    // 給全部的小圓點註冊點擊事件
    for (var i = 0; i < lis.length; i++) {
        lis[i].index = i;
        lis[i].onclick = function() {
            // 小圓點高亮排他
            for (var i = 0; i < lis.length; i++) {
                lis[i].className = "";
            }
            this.className = "current";
            // 點擊小圓點,讓對應的圖片輪播 獲取ul要改變的距離
            // 負的表示ul 向左運動 此時小圓點對應的索引乘以盒子的寬度 就是ul要移動的寬度
            var target = -this.index * imgWidth;
            // ul.style.left = target + 'px';
            // 讓圖片像動畫同樣慢慢的移過去
            animate(ul, target, 50);
        }

    }
</script>

效果圖:

image

從上面效果圖中,咱們能夠看到,一個最簡單的輪播圖已經成型了,可是須要去用手點擊,並且若是跨點數去點擊,會發現圖片要一張張滑過去,這裏後面咱們會優化。

3.2 左右焦點輪播圖

左右焦點輪播圖,就是在顯示圖片的兩端添加兩個按鈕,一個向左,一個向右,點擊的時候圖片會根據點擊的方向滑動。而且當鼠標懸停在顯示區域的時候,兩個按鈕顯示。鼠標離開顯示區域,,兩個按鈕隱藏。

示例代碼: [09-左右焦點輪播圖.html]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
        list-style: none;
    }
    
    #slide {
        width: 560px;
        height: 315px;
        margin: 100px auto;
        position: relative;
        overflow: hidden;
    }
    
    #slide ul {
        width: 600%;
        position: absolute;
    }
    
    #slide ul li {
        float: left;
    }
    
    #slide ul img {
        display: block;
    }
    
    #slide #arrow {
        display: none;
    }
    
    #slide #arrow #leftArr,
    #slide #arrow #rightArr {
        width: 30px;
        height: 60px;
        background-color: rgba(255, 255, 2550, 0.3);
        position: absolute;
        top: 50%;
        margin-top: -30px;
        text-decoration: none;
        color: #fff;
        text-align: center;
        font: 700 24px/60px "宋體";
    }
    
    #slide #arrow #leftArr {
        left: 0;
    }
    
    #slide #arrow #rightArr {
        right: 0;
    }
</style>

<!-- html 部分 -->
<div id="slide">
    <ul>
        <li>
            <a href="#"><img src="../image/1.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/2.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/3.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/4.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/5.jpg" alt=""></a>
        </li>
    </ul>

    <div id="arrow">
        <a href="javascript:void(0);" id="leftArr">&lt;</a>
        <a href="javascript:void(0);" id="rightArr">&gt;</a>
    </div>
</div>

<!-- js 部分 -->
<script src="../js/animate.js"></script>
<script>
    var slide = document.getElementById('slide');
    var ul = slide.children[0];
    var lis = ul.children;
    var arrow = document.getElementById('arrow');
    var leftArr = document.getElementById("leftArr");
    var rightArr = document.getElementById("rightArr");

    var imgWidth = slide.offsetWidth;

    // 給slide註冊鼠標通過事件,鼠標通過時 顯示arrow
    slide.onmouseover = function() {
        arrow.style.display = "block";
    };
    // 給slide註冊鼠標離開事件,鼠標離開時 隱藏arrow
    slide.onmouseout = function() {
        arrow.style.display = "none";
    };
    // 點擊右箭頭
    var count = 0; // 跑出去的張數
    rightArr.onclick = function() {
        // 當這個張數不等於最後一張的時候 執行動畫
        if (count < lis.length - 1) {
            count++;
            var target = -count * imgWidth;
            animate(ul, target, 40);
        }
    }
    leftArr.onclick = function() {
        // 當這個張數不等於最後一張的時候 執行動畫
        if (count > 0) {
            count--;
            var target = -count * imgWidth;
            animate(ul, target, 40);
        }
    }
</script>

效果圖:

image

3.3 無縫輪播圖

上圖能夠看到,當滑到最左邊或者最右邊的時候,再點擊就沒有用了,正常的輪播圖確定不是這樣的,點擊到最後一張後再點擊確定是接着滑動的。下面咱們接着看,如何實現一個無縫輪播圖

示例代碼:無縫輪播(能夠一直點擊) [10-左右焦點輪播圖-無縫滾動.html]

何謂無縫滾動?

無縫滾動就是圖片可以循環切換,就算是最後一張,點擊以後也會跳到第一張

原理:

  • 效果就像上面所說的同樣,主要實現原理就是,在最後面一張圖片,再加上一張圖片,這張圖片就是第一張圖片
  • 當滑動到最後一張圖片的時候(看下圖),此時的視覺效果就是停在第一張圖片上
  • 這時只須要在程序上判斷,當在最後一張的時候,直接跳到第一張圖片便可

image

示例代碼:無縫滾動的簡單原理 [10-無縫滾動原理.html]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
        list-style: none;
    }
    
    #slide {
        position: relative;
        width: 560px;
        height: 315px;
        border: 6px dashed #CBF078;
        margin: 100px auto;
        overflow: hidden;
    }
    
    #slide ul {
        width: 3360px;
        position: absolute;
        left: 0;
        top: 0;
    }
    
    #slide ul li {
        float: left;
    }
    
    #slide ul li img {
        display: block;
        vertical-align: top;
    }
</style>

<!-- html 部分 -->
<div id="slide">
    <ul>
        <li>
            <img src="../image/1.jpg" alt="">
        </li>
        <li>
            <img src="../image/2.jpg" alt="">
        </li>
        <li>
            <img src="../image/3.jpg" alt="">
        </li>
        <li>
            <img src="../image/4.jpg" alt="">
        </li>
        <li>
            <img src="../image/5.jpg" alt="">
        </li>
        <!-- 添加一張與第一張如出一轍的圖片 障眼法 -->
        <li>
            <img src="../image/1.jpg" alt="">
        </li>
    </ul>
</div>

<!-- js 部分 -->
<script>
    var slide = document.getElementById('slide');
    var ul = slide.children[0];
    setInterval(function() {
        // 每次向左移動的距離
        var step = -3;
        // 獲取 ul的left的值 是個負值
        var leader = ul.offsetLeft;
        // 定義一個目標距離,這裏的目標距離指的是最後一張圖片距離左邊的left值
        // 圖片寬度560 在最後一張距離左邊left的位置:-560*5 = -2800
        // 就是說當到達這張圖片的時候就應該讓  ul.style.left = "0px";
        var target = -2800;
        // 爲何不直接判斷 leader = -2800的時候讓ul.style.left = "0px";?
        // 由於每次走3步 3不能被2800整除,因此leader永遠不會等於-2800的
        // 這裏直接判斷leader此時距左邊的距離減去目標距離當這個絕對值大於等於 一步距離的絕對值3的時候讓它執行往左運動
        if (Math.abs(leader - target) >= Math.abs(step)) {
            leader = leader + step;
            ul.style.left = leader + "px";
        // 當不足一步距離的時候說明就是最後一張了,就應該跳到第一張圖片了
        } else {
            ul.style.left = "0px";
        }
    }, 15);
</script>

效果圖:

image

左右焦點無縫輪播圖: [11-左右焦點無縫輪播圖.html]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
        list-style: none;
    }
    
    #slide {
        width: 560px;
        height: 315px;
        margin: 100px auto;
        position: relative;
        overflow: hidden;
    }
    
    #slide ul {
        width: 600%;
        position: absolute;
    }
    
    #slide ul li {
        float: left;
    }
    
    #slide ul img {
        display: block;
    }
    
    #slide #arrow {
        display: none;
    }
    
    #slide #arrow #leftArr,
    #slide #arrow #rightArr {
        width: 30px;
        height: 60px;
        background-color: rgba(255, 255, 2550, 0.3);
        position: absolute;
        top: 50%;
        margin-top: -30px;
        text-decoration: none;
        color: #fff;
        text-align: center;
        font: 700 24px/60px "宋體";
    }
    
    #slide #arrow #leftArr {
        left: 0;
    }
    
    #slide #arrow #rightArr {
        right: 0;
    }
</style>

<!-- html 部分-->
<div id="slide">
    <ul>
        <li>
            <a href="#"><img src="../image/1.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/2.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/3.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/4.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/5.jpg" alt=""></a>
        </li>
        <!-- 添加一張圖片 障眼法 -->
        <li>
            <a href="#"><img src="../image/1.jpg" alt=""></a>
        </li>
    </ul>

    <div id="arrow">
        <a href="javascript:void(0);" id="leftArr">&lt;</a>
        <a href="javascript:void(0);" id="rightArr">&gt;</a>
    </div>
</div>

<!-- js 部分 -->
<script src="../js/animate.js"></script>
<script>
    var slide = document.getElementById('slide');
    var ul = slide.children[0];
    var lis = ul.children;
    var arrow = document.getElementById('arrow');
    var leftArr = document.getElementById("leftArr");
    var rightArr = document.getElementById("rightArr");

    var imgWidth = slide.offsetWidth;

    // 給slide註冊鼠標通過事件,鼠標通過時 顯示arrow
    slide.onmouseover = function() {
        arrow.style.display = "block";
    };
    // 給slide註冊鼠標離開事件,鼠標離開時 隱藏arrow
    slide.onmouseout = function() {
        arrow.style.display = "none";
    };
    // 點擊右箭頭
    var count = 0; // 跑出去的張數
    rightArr.onclick = function() {
        // 當這個張數等於最後一張的時候,偷偷摸摸的把最後一張圖片換成第一張
        if (count == lis.length - 1) {
            count = 0;
            ul.style.left = 0;
        }
        count++;
        var target = -count * imgWidth;
        animate(ul, target, 40);
    }
    leftArr.onclick = function() {
        // 判斷是第一張的時候,偷偷摸摸的把第一張換成最後一張
        if (count == 0) {
            count = lis.length - 1;
            ul.style.left = -count * imgWidth + "px";
        }
        count--;
        var target = -count * imgWidth;
        animate(ul, target, 40);
    }
</script>

效果圖:

image

3.4 完整版輪播圖

前面咱們已經能夠經過點擊對應的小點、左右焦點和無縫滾動來實現輪播圖了,不過都是單獨分開來的,如今咱們作個整合,實現一個完整的輪播圖。

功能概述:

  • 簡單輪播功能

    • circle下的全部的li註冊點擊事件
    • 排他
    • 移動Ul
  • 左右焦點功能

    • 須要定義一個變量count來記錄移動的圖片的張數。
  • 點擊右箭頭功能

    • 若是當前圖片是最後一張(假圖片),須要瞬間變成真圖片
    • 點擊一次,須要讓圖片往右移動一張
    • 同步小圓點,幹掉全部小圓點,復活對應count的小圓點。
    • 最後一張假圖片對應的小圓點是第一個,須要作特殊處理
點擊左箭頭的功能和右箭頭基本一致。
  • 自動輪播的功能

    • 開啓定時器,每隔兩秒點擊一次右箭頭
    • 鼠標通過盒子,中止定時器(箭頭亂閃的問題解釋)觸發事件的必定要是外面的大盒子,不能是ul,若是給ul註冊事件,就會出現亂閃的問題
    • 鼠標離開盒子,開啓定時器
  • 同步功能

    • 點擊小圓點時須要同步
    • 淘寶bug解決方法(當一圈事後回到第一個小圓點的時候,再點擊它會發現他會再跑一圈)
  • 淘寶bug圖:

淘寶bug

完整代碼: [12-完整版輪播圖.html]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
        list-style: none;
    }
    
    #slide {
        width: 560px;
        height: 315px;
        margin: 100px auto;
        position: relative;
        overflow: hidden;
    }
    
    #slide ul {
        width: 600%;
        position: absolute;
    }
    
    #slide ul li {
        float: left;
    }
    
    #slide ul img {
        display: block;
    }
    
    #slide #arrow {
        display: none;
    }
    
    #slide #arrow #leftArr,
    #slide #arrow #rightArr {
        width: 30px;
        height: 60px;
        background-color: rgba(0, 0, 0, 0.5);
        position: absolute;
        top: 50%;
        margin-top: -30px;
        text-decoration: none;
        color: #fff;
        text-align: center;
        font: 700 24px/60px "宋體";
    }
    
    #slide #arrow #leftArr {
        left: 0;
    }
    
    #slide #arrow #rightArr {
        right: 0;
    }
    
    #slide ol {
        width: 100px;
        height: 14px;
        background-color: rgba(255, 255, 255, .6);
        /* background-color: pink; */
        position: absolute;
        bottom: 14px;
        left: 50%;
        margin-left: -50px;
        border-radius: 7px;
    }
    
    #slide ol li {
        width: 10px;
        height: 10px;
        float: left;
        background-color: #fff;
        border-radius: 50%;
        margin-top: 2px;
        margin-left: 8.5px;
        cursor: pointer;
    }
    
    #slide ol li.current {
        background-color: #DF654A;
    }
</style>

<!--html 部分-->
<div id="slide">
    <ul>
        <li>
            <a href="#"><img src="../image/1.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/2.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/3.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/4.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/5.jpg" alt=""></a>
        </li>
        <li>
            <a href="#"><img src="../image/1.jpg" alt=""></a>
        </li>
    </ul>
    <!-- 左右箭頭 -->
    <div id="arrow">
        <a href="javascript:void(0);" id="leftArr">&lt;</a>
        <a href="javascript:void(0);" id="rightArr">&gt;</a>
    </div>
    <!-- 小圓點 -->
    <ol id="circleOl">
        <li class="current"></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
    </ol>
</div>


<script src="../js/animate.js"></script>
<script>
    // 自執行函數,防止頁面其餘定時器會受影響
    (function() {
        var slide = document.getElementById('slide');
        var imgUl = slide.children[0];
        var imgLis = imgUl.children;
        var arrow = document.getElementById('arrow');
        var leftArr = document.getElementById("leftArr");
        var rightArr = document.getElementById("rightArr");
        var circleOl = document.getElementById('circleOl');
        var circleLis = circleOl.children;
        // 獲取圖片的寬度
        var imgWidth = slide.offsetWidth;
        var timer = null;

        // 點擊小圓點改變對應圖片
        for (var i = 0; i < circleLis.length; i++) {
            circleLis[i].index = i;
            circleLis[i].onclick = function() {
                // 小圓點點擊的時候高亮排他
                for (var i = 0; i < circleLis.length; i++) {
                    circleLis[i].className = "";
                }
                this.className = "current";
                // 淘寶bug:這時還須要判斷一下 就是當圖片在最後一張假圖片的時候,
                // 再去點擊第一個小圓點的時候,會出現一個bug,就是圖片會輪播一圈再回到這張圖片上
                if (count == imgLis.length - 1) {
                    count = 0;
                    imgUl.style.left = 0;
                }
                // 點擊小圓點圖片要移動
                var target = -this.index * imgWidth;
                // 若是這裏不記錄一下,當點擊小圓點跳到某張圖片的時候,再自動播放的時候,
                // 不會接着當前小圓點的位置日後播放,而是接着以前count不變的狀況下 繼續播放的
                count = this.index;
                animate(imgUl, target, 40);
            }
        }

        // 左右焦點輪播圖
        var count = 0; // 跑出去的張數
        rightArr.onclick = function() {
            // 當這個張數等於最後一張(假圖片)的時候,偷偷摸摸的把最後一張圖片換成第一張
            if (count == imgLis.length - 1) {
                count = 0;
                imgUl.style.left = 0;
            }
            // 點擊一次圖片向右划動一次
            count++;
            var target = -count * imgWidth;
            animate(imgUl, target, 40);

            //讓小圓點跟着動 只要將 count 與小圓點綁定便可
            for (var i = 0; i < circleLis.length; i++) {
                circleLis[i].className = "";
            }
            // 這裏須要判斷一下 由於此時最後一張是假圖片 小圓點是不能正常跳轉到第一個的
            // 當count == 最後一張圖片的下標的時候,直接讓第一個小圓點亮
            if (count == imgLis.length - 1) {
                circleLis[0].className = "current";
            } else {
                // 不然其餘的下標對應的小圓點高亮
                circleLis[count].className = "current";
            }
        }
        leftArr.onclick = function() {
            // 判斷是第一張的時候,偷偷摸摸的把第一張換成最後一張
            if (count == 0) {
                count = imgLis.length - 1;
                imgUl.style.left = -count * imgWidth + "px";
            }
            count--;
            var target = -count * imgWidth;
            animate(imgUl, target, 40);

            // 小圓點同步 往左的時候不會出現小圓點不一樣步的問題
            for (var i = 0; i < circleLis.length; i++) {
                circleLis[i].className = "";
            }
            circleLis[count].className = "current";
        }

        timer = setInterval(function() {
            rightArr.onclick();
        }, 2000);
        // 給slide註冊鼠標通過事件,鼠標通過時 顯示arrow
        slide.onmouseover = function() {
            arrow.style.display = "block";
            // 鼠標通過圖片的時候清除定時器,中止輪播
            clearInterval(timer);
        };
        // 給slide註冊鼠標離開事件,鼠標離開時 隱藏arrow
        slide.onmouseout = function() {
            arrow.style.display = "none";
            // 鼠標離開圖片的時候開啓定時器,自動輪播
            timer = setInterval(function() {
                rightArr.onclick();
            }, 2000);
        };

    })()

輪播圖的一些功能可能有點繞,寫的有可能不太看得懂,有疑惑的小夥伴直接私信我,我會一步步解釋給你聽的,有什麼bug也能夠將代碼發給我。

想爲後面打下紮實的基礎的話,這裏必定要多敲幾遍,主要要搞明白的是實現的思路,以及一些小bug的解決。這對後面的學習是很是重要的。代碼備註可能讀起來有些拗口,不懂得小夥伴直接私信給我。

4. 緩動動畫框架

4.1 緩動動畫初體驗

緩動動畫,顧名思義,就是愈來愈慢的運動。

咱們先來回顧一下上面勻速動畫運動的原理:

/**
    step : 一步的距離
    leader :當前的距離
    咱們能夠看到 step在這裏一直等於5  未曾改變 因此就是勻速運動
*/
var step = 5;
leader = leader + step;

如今咱們再來看下緩動動畫的原理:

/** 
    target: 目標距離,盒子運動到什麼地方
    step  : 一樣的,仍是指每次運動的距離,可是這裏的步數是一個變化的量了,
             咱們能夠看到它會隨着leader的增長變得愈來愈小,這就是緩動動畫的原理
    leader: 當前的距離
*/
var step = (target - leader)/10;
leader = leader + step;

示例代碼: [13-緩動動畫初體驗(一).html]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
    }
    
    #box {
        width: 100px;
        height: 100px;
        position: absolute;
        background: orange;
    }
</style>

<!-- html 部分-->
<input type="button" value="奔跑吧" id="btn">
<div id="box"></div>

<!-- js 部分 -->
<script>
    var box = document.getElementById('box');
    var btn = document.getElementById('btn');
    var timer = null;

    btn.onclick = function() {
        clearInterval(timer);
        timer = setInterval(function() {
            // 定義一個目標距離
            var target = 600;
            // 得到當前盒子的位置
            var leader = box.offsetLeft;
            // 每次運動的距離
            var step = (target - leader) / 10;
            // leader = leader + step  動起來
            leader += step;
            // 將距離給盒子
            box.style.left = leader + "px";
            // 噹噹前距離等於目標距離的時候清除定時器
            if (leader == target) {
                clearInterval(timer);
            }
        }, 15);
    }
</script>

效果圖:

image

完美了嗎?並無,這裏有個小bug:

可能會有小夥伴不理解,有問題你上面直接講一下不就得了,還特意賣關子在下面從新寫一遍。我想跟你們說的一點就是,若是在上面我直接告訴你這裏有個問題有個bug的話,你一眼看過,可能都不當回事,我在這裏拿出來說一下,相信這個知識點你會記得更深。

小bug:明明設置的是600,怎麼會是596.4px呢?

image

緣由:

  • offsetLeft獲取值的時候,只會獲取整數,會對小數部分會四捨五入處理,好比step = (target - leader)/10step的值出現小數的時候,leader+= step以後,offsetLeft在獲取leader位置的時候就會把小數部分四捨五入,這樣就會形成最後距離的偏差。

解決方法:

  • step向上取整處理(Math.ceil()),保證每一次都至少跑1px的距離,只要不出現小數offsetLeft就不會出現四捨五入。

完整代碼: [14-緩動動畫初體驗(二).html]

var box = document.getElementById('box');
var btn = document.getElementById('btn');
var timer = null;

btn.onclick = function() {
    clearInterval(timer);
    timer = setInterval(function() {
        // 定義一個目標距離
        var target = 600;
        // 得到當前盒子的位置
        var leader = box.offsetLeft;
        // 每次運動的距離
        var step = (target - leader) / 10;
        // 對step進行向上取整
        step = Math.ceil(step);
        // leader = leader + step  動起來
        leader += step;
        // 將距離給盒子
        box.style.left = leader + "px";
        // 噹噹前距離等於目標距離的時候清除定時器
        if (leader == target) {
            clearInterval(timer);
        }
    }, 15);
}

4.2 緩動動畫函數封裝

前面勻速動畫那裏已經講過封裝一個函數的好處與重要性,如今咱們將緩動動畫也封裝成一個函數。

示例代碼: [15-緩動動畫函數封裝.html]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
    }
    
    #box {
        width: 100px;
        height: 100px;
        background: orange;
        position: absolute;
    }
</style>

<!-- html 部分 -->
<input type="button" value="奔跑吧500" id="btn1">
<input type="button" value="奔跑吧1000" id="btn2">
<div id="box"></div>

<!-- js 部分 -->
<script>
    var btn1 = document.getElementById('btn1');
    var btn2 = document.getElementById('btn2');
    var box = document.getElementById('box');
    
    // 緩動動畫函數
    /**
        element : 執行動畫元素
        target  : 目標距離
        num     : 用來控制動畫執行的速度 越大動畫執行越慢
    */
    function slowAnimate(element, target, num) {
        // 一進來就要清除定時器,防止越點越快
        clearInterval(element.timer);
        element.timer = setInterval(function() {
            // 得到元素當前位置
            var leader = element.offsetLeft;
            // 定義每次運動的距離
            var step = (target - leader) / num;
            // step多是小數 因此要取整
            step = Math.ceil(step);
            leader += step;
            // 設置元素的位置
            element.style.left = leader + 'px';
            // 當元素的位置 等於 目標位置的時候 清除定時器
            if (leader == target) {
                clearInterval(element.timer);
            }
        }, 15);
    }
    // 調用緩動動畫函數
    btn1.onclick = function() {
        slowAnimate(box, 500, 10);
    }
    // 一樣是運動500的距離,咱們能夠發現從500到1000,明顯執行的比從0-500執行的慢
    btn2.onclick = function() {
        slowAnimate(box, 1000, 30);
    }
</script>

效果圖:

image

又到了找bug的時候了:

上面的代碼從0-500,從500-1000都沒有問題,通過向上取整後都能到達目標距離:5001000。可是小夥伴能夠看下,當從1000回到500的時候,是正好回到500的嗎?答案確定不是的,爲何呢?

image

step爲正數的時候,向上取整是徹底沒有問題的,可是當從1000500的時候,step就是負數了,負數向上取整後就會變得更大,好比本來是-33.3,向上取整後就是-33了,-0.3就會捨去,全部就不會到500的位置。

解決方法: 判斷step的正負,爲正的時候,向上取整。爲負的時候,向下取整。

緩動函數封裝完整版: [16-緩動動畫函數封裝完整版.html]

function slowAnimate(element, target, num) {
    // 一進來就要清除定時器,防止越點越快
    clearInterval(element.timer);
    element.timer = setInterval(function() {
        // 得到元素當前位置
        var leader = element.offsetLeft;
        // 定義每次運動的距離
        var step = (target - leader) / num;
        //若是step是正數,對step向上取整,
        //若是step是負數,對step向下取整
        // 保證每一次最少都走1px
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        leader += step;
        // 設置元素的位置
        element.style.left = leader + 'px';
        // 當元素的位置 等於 目標位置的時候 清除定時器
        if (leader == target) {
            clearInterval(element.timer);
        }
    }, 15);
};

4.3 獲取元素計算後的樣式

獲取元素計算後的樣式指的是元素通過層疊後真正生效的樣式,無論樣式寫在哪,計算後的樣式指的就是最終的樣式。

經過style只能獲取到寫在行內的樣式,那麼想要獲取其餘的樣式怎麼辦呢?

  • js提供了一個方法:window.getComputedStyle(element, null)[attr];,它返回的是一個對象CSSStyleDeclaration[attr]就是這個對象裏面就是計算後的全部的樣式的屬性名(關聯數組取對象的值)。element指的是當前參數,null

這裏能夠不用深究-官方解釋。這個方法須要window調用。

/**
    element :獲取樣式的當前元素
    null    :這裏能夠傳一個僞元素,若是不是僞元素的話必須是null
    attr    :後面能夠寫具體的屬性,好比boderRadius  就會獲取這個元素的border-radius樣式信息
*/
window.getComputedStyle(element,null)[attr];

image

示例代碼: [17-獲取元素計算後的樣式.html]

<!-- 樣式部分 -->
<style>
    div {
        width: 100px;
        height: 100px;
        background: pink;
    }
    #box {
        width: 200px;
    }
</style>

<!-- html 部分 -->
<div id="box" style="width:300px;"></div>

<!-- js 部分 -->
<script>
    var box = document.getElementById('box');
    console.log(window.getComputedStyle(box, null)); // 打印得到box的各類屬性的樣式
    // 其中行內樣式權重最高,因此最後得到的寬應該是300px
    console.log(window.getComputedStyle(box, null).width); // 300px
    console.log(window.getComputedStyle(box, null).background);
    
</script>

效果圖:

image

兼容性處理:

  • window.getComputedStyle(element, null)[attr];只適用於現代瀏覽器中
  • IE678有本身的方法:element.currentStyle[attr];
// 獲取元素計算後的樣式
function getStyle(element,attr){
    if(window.getComputedStyle){
        return window.getComputedStyle(element, null)[attr];
    }else{
        return element.currentStyle[attr];
    }
}

// 注意:調用函數的時候 獲取的屬性名是一個字符串
alert(getStyle(box, "width"));

[18-獲取元素計算後的樣式兼容性處理.html]

注意: 上面的封裝函數中,調用的時候,屬性名是一個字符串類型。

4.4 緩動動畫修改多個樣式

無論是上面的勻速動畫函數,仍是這裏的緩動動畫函數,都只能左右運動,可是一個真正完整的動畫函數,只改變左右位置確定是不夠的,咱們可能須要改變它的寬高等。在上面一節中,咱們知道了如何獲取到元素計算後的樣式,並且只要是元素有的樣式都能獲取到,有了這個方法咱們就可讓動畫去執行更多的事情了。

一、對獲取到的樣式返回值進行處理:

在上面的一節中,咱們能夠看到,獲取的返回值都是字符串格式,好比獲取寬度的時候,返回的是一個 "300px"的字符串,由於緩動動畫函數裏面是須要計算的,這裏是個字符串確定不行,因此咱們須要對其進行 parseInt取整處理。

[19-緩動動畫修改多個樣式-處理返回值.html]:

function getStyle(element, attr) {
    if (window.getComputedStyle) {
        return window.getComputedStyle(element, null)[attr];
    } else {
        return element.currentStyle[attr];
    }
}

function animate(element, attr, target) {
    clearInterval(element.timer);
    element.timer = setInterval(function() {
        // getStyle 返回的是樣式屬性的值 咱們用一個變量將它儲存起來
        var leader = getStyle(element, attr);
    
        // 由於返回值是一個字符串,而且帶有字符px,因此咱們對返回值進行取整轉換
        leader = parseInt(leader) || 0;  // 這裏或 0的目的就是,當parseInt取整失敗的話,給一個默認值0
        
        var step = (target - leader) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        leader += step;
    
        // 設置指定樣式
        element.style[attr] = leader + "px";
    
        if (leader == target) {
            clearInterval(element.timer);
        }
    }, 15);
}

animate(box, "left", 800);

上面的代碼咱們對它的返回值進行了處理,並且還能夠對它設置其餘的樣式,只要單位是px的屬性均可以設置。可是這裏每次仍是隻能設置一個樣式,下面咱們來實現修改多個樣式。

注意: leader = parseInt(leader) || 0; "或"上0的目的就是:當有些屬性設置的值不是數字的時候,好比:auto,這時候parseInt轉換的結果是NaN。當"或"上0以後,轉換失敗後,leader,就會默認是0

二、遍歷一個對象:

讓咱們來複習一下,js基礎的時候,咱們接觸到了對象,而且知道了能夠用 for..in的方法來遍歷對象。咱們知道 getComputedStyle方法,獲取計算後樣式的時候,返回的是一個名叫 CSSStyleDeclaration的對象,這個對象裏面是全部的樣式屬性,咱們想要對這些屬性進行多個操做的時候,就能夠經過遍歷的方法。
for(k in obj){
    // k    :就是至關於對象的鍵
    // obj  :就是須要遍歷的對象
}

三、同時修改多個樣式:

同時修改多個樣式,就是將要修改的多個屬性以對象的形式做爲參數傳進函數中。

[20-緩動動畫修改多個樣式.html]

var box = document.getElementById('box');
var btn = document.getElementById('btn');

// 封裝一個函數,element 表示執行動畫的元素  obj傳的是一個對象,裏面能夠設置多個屬性和值
function animate(element, obj) {
    clearInterval(element.timer);
    element.timer = setInterval(function() {
        // 遍歷外部傳進來的對象
        for (k in obj) {
            //attr   :  要作動畫的樣式
            //target :  目標值
            var attr = k;
            var target = obj[k];
            // 獲取元素開始時計算後的樣式
            var leader = getStyle(element, attr);
            leader = parseInt(leader) || 0;
            // 緩動動畫函數原理
            var step = (target - leader) / 10;
            step = step > 0 ? Math.ceil(step) : Math.floor(step);
            leader += step;

            // 給元素設置以樣式屬性名爲attr的值  
            // 這個封裝的動畫函數只能改值是px單位的樣式
            element.style[attr] = leader + "px";
            if (leader == target) {
                clearInterval(element.timer);
            }
        }
    }, 15);
}

// 處理兼容性
function getStyle(element, attr) {
    if (window.getComputedStyle) {
        return window.getComputedStyle(element, null)[attr];
    } else {
        return element.currentStyle[attr];
    }
}
// 調用函數 設置了五個樣式屬性
btn.onclick = function() {
    animate(box, {
        width: 200,
        height: 200,
        left: 300,
        top: 300,
        // bprder-radius 應該轉爲駝峯命名法 而且值只能是100px的格式  不能是百分比
        borderRadius: 100
    });
}

效果圖:

image

經過上面封裝的函數咱們能夠改變多個樣式,可是效果圖中咱們能夠看到一個問題,就是當到達設定值後,點擊按鈕還會慢慢的抖動。緣由是修改多個樣式的時候,全部的樣式並不能都到同時達終點。

4.5 緩動動畫修復定時器bug

出現這個bug的緣由:在for循環中判斷是否到達目標值,到達後就清除定時器,可是咱們同時修改了5個樣式,可能有的樣式到達目標值後就清楚定時器了,可是有的樣式還沒到達目標值,因此就出現了上面的 bug

解決方法:假設成立法

  • 假設成立
  • 想辦法推翻假設
  • 若是推翻不了,說明假設成立

示例代碼: [21-緩動動畫修改多個樣式-修復定時器bug.html]

function animate(element, obj) {
    clearInterval(element.timer);
    element.timer = setInterval(function() {
        // 1-假設都到達了終點
        var flag = true;
        for (k in obj) {
            var attr = k;
            var target = obj[k];
            var leader = getStyle(element, attr);
            leader = parseInt(leader) || 0;
            var step = (target - leader) / 10;
            step = step > 0 ? Math.ceil(step) : Math.floor(step);
            leader += step;
            element.style[attr] = leader + "px";

            // 2- 必需要等到全部的樣式都到達終點才清除定時器
            //    只要有一個樣式沒有到達設定值,說明假設失敗
            if (leader != target) {
                flag = false;
            }
        }
        // 全部的樣式都到達終點後 清除定時器
        if (flag) {
            clearInterval(element.timer);
        }
    }, 15);
}

4.6 緩動動畫兼容其它樣式屬性

通過前面幾小節的學習,咱們已經能夠實現同時修改多個樣式的緩動動畫了。可是細心的小夥伴不知道有沒有發現,目前只能設置跟 px有關係的樣式,包括設置 border-radiu也不算完善。這是由於咱們緩動動畫封裝的時後,設置的 element.style[attr] = leader + "px";,因此只能實現跟 px有關的樣式。

設置兼容其餘屬性的時候,要注意兩點,第一獲取的時候要進行判斷,設置的時候也要進行判斷

一、兼容opacity屬性: [22-緩動動畫修改多個樣式-兼容opacity.html]

function animate(element, obj) {
    clearInterval(element.timer);
    element.timer = setInterval(function() {
        var flag = true;
        for (k in obj) {
            var attr = k;
            var target = obj[k];
            // 判斷得到的屬性是否是「opacity」,是的話單獨處理
            var leader;
            // 得到當前值
            if (attr === "opacity") {
                // 獲取的時候是個小數,將它乘以100 運算時不會出現精度丟失
                leader = getStyle(element, attr) * 100 || 1;
            } else {
                leader = getStyle(element, attr);
                leader = parseInt(leader) || 0;
            }

            var step = (target - leader) / 10;
            step = step > 0 ? Math.ceil(step) : Math.floor(step);
            leader += step;
            // 賦值
            // 判斷是否是opacity屬性 是的話 單獨賦值
            if (attr === "opacity") {
                // 由於開始的時候leader擴大了100倍 設置的時候 opacity只能是0-1
                element.style[attr] = leader / 100;
                // opacity 還須要單獨處理,由於IE678 不支持opacity 
                element.style.filter = "alpha(opacity=" + leader + ")";
            } else {
                element.style[attr] = leader + "px";
            }
            if (leader != target) {
                flag = false;
            }
        }
        if (flag) {
            clearInterval(element.timer);
        }
    }, 15);
}

// 處理獲取樣式兼容性
function getStyle(element, attr) {
    if (window.getComputedStyle) {
        return window.getComputedStyle(element, null)[attr];
    } else {
        return element.currentStyle[attr];
    }
}

// 調用這個函數
btn.onclick = function() {
    animate(box, {
        width: 200,
        height: 200,
        left: 300,
        top: 300,
        // 這裏是按照 0-100 設置不透明度的,由於小數計算的時候會出現精度丟失
        opacity: 50
    });
}

二、兼容zIndex屬性: [23-緩動動畫修改多個樣式-兼容zIndex.html]

zIndex這個屬性不須要緩動的執行改變層級,直接得到傳進來的值設置便可
// 賦值
if (attr === "opacity") {
    element.style[attr] = leader / 100;
    element.style.filter = "alpha(opacity=" + leader + ")";
// 判斷設置的時候是不是zIndex屬性
} else if (attr === "zIndex") {
    element.style.zIndex= leader;
} else {
    element.style[attr] = leader + "px";
}

示例代碼: [24-緩動動畫淡入淡出效果.html]

btn1.onclick = function() {
    animate(box, {
        opacity: 100
    })
}
btn2.onclick = function() {
    animate(box, {
        opacity: 0
    })
}

效果圖:

image

4.7 緩動動畫添加回調函數

程序執行完畢,再次執行的函數。

示例代碼: [25-緩動動畫添加回調函數.html]

var box = document.getElementById('box');
var btn = document.getElementById('btn');

function animate(element, obj, fn) {
    clearInterval(element.timer);
    element.timer = setInterval(function() {
        var flag = true;
        for (k in obj) {
            var attr = k;
            var target = obj[k];
            var leader = getStyle(element, attr);
            leader = parseInt(leader) || 0;
            var step = (target - leader) / 10;
            step = step > 0 ? Math.ceil(step) : Math.floor(step);
            leader += step;
            element.style[attr] = leader + "px";
            if (leader != target) {
                flag = false;
            }
        }
        if (flag) {
            clearInterval(element.timer);
            // 全部程序執行完畢了,如今能夠執行回調函數了
            // 只有傳遞了回調函數,才能執行,因此這裏要判斷一下
            if (fn) {
                fn();
            }
            /* fn&&fn(); */
        }
    }, 15);
}

// 處理兼容性
function getStyle(element, attr) {
    if (window.getComputedStyle) {
        return window.getComputedStyle(element, null)[attr];
    } else {
        return element.currentStyle[attr];
    }
}
// 調用函數 
btn.onclick = function() {
    animate(box, {
        left: 600
    }, function() {
        animate(box, {
            top: 500,
            borderRadius: 50
        }, function() {
            animate(box, {
                width: 400,
                borderRadius: 50
            });
        });
    });
}

效果圖:

image

5. 筋斗雲案例

直接看效果圖:

image

效果如上圖,當咱們鼠標通過某一項時,小方塊會緩動移過去,當離開列表欄時,小方塊會回到最初的位置。當點擊某一項時小方塊的初始位置就會停留在該項上。

示例代碼: [26-筋斗雲案例.html]

<!-- 樣式部分 -->
<style>
    body {
        padding: 0;
        margin: 0;
        background: #333;
    }
    #box {
        width: 800px;
        height: 34px;
        margin: 100px auto;
        background: orange;
        position: relative;
    }
    ul {
        padding: 0 50px;
        height: 34px;
        position: relative;
    }
    #box ul li {
        float: left;
        width: 100px;
        height: 34px;
        line-height: 34px;
        text-align: center;
        list-style: none;
        font-size: 18px;
        font-family: '方正';
        color: #fff;
        cursor: pointer;
    }
    #over {
        position: absolute;
        top: -3px;
        left: 51px;
        width: 100px;
        height: 38px;
        background: orangered;
    }
</style>

<!-- html 部分 -->
<div id='box'>
    <span id='over'></span>
    <ul id='nav'>
        <li>首頁</li>
        <li>社區服務</li>
        <li>智慧超市</li>
        <li>便民</li>
        <li>圈子</li>
        <li>活動</li>
        <li>聚優惠</li>
    </ul>
</div>

<!-- js 部分 -->
<script>
    var over = document.getElementById('over');
    var nav = document.getElementById('nav');
    var lis = nav.children;

    for (var i = 0; i < lis.length; i++) {
        lis[i].onmouseover = function() {
                // 鼠標通過時移動的距離就是它距離左邊的距離
                slowAnimate(over, this.offsetLeft);
            }
            // 設定默認位置,由於第一個選項距離左邊爲51px距離因此,默認值設置爲51
        var staticLeft = 51;
        lis[i].onmouseout = function() {
            // 鼠標離開的時候,要讓它回到默認位置
            slowAnimate(over, staticLeft);
        }

        lis[i].onclick = function() {
            // 當點擊某一選項的時候,將默認位置設置爲此時的位置
            staticLeft = this.offsetLeft;
        }
    }
    // 緩動動畫
    function slowAnimate(element, target, num) {
        clearInterval(element.timer);
        element.timer = setInterval(function() {
            var leader = element.offsetLeft;
            // num 不傳的話,默認是10
            var step = (target - leader) / (num || 10);
            step = step > 0 ? Math.ceil(step) : Math.floor(step);
            leader += step;
            element.style.left = leader + 'px';
    
            if (leader == target) {
                clearInterval(element.timer);
            }
        }, 15);
    }
</script>

6. 右下角關閉廣告案例

在網頁中常常會出現廣告,咱們舉個例子讓關閉廣告的時候有一個動畫效果。

實現原理:

  • 圖片其實被切成了兩個部分,看到的效果是一張圖片,實際上是兩張。
  • 點擊關閉按鈕的時候,調用緩動動畫函數,將下半部分的盒子高度等於0,因此會出現一個向下的效果
  • 在剛剛的動畫函數的回調函數裏面繼續調用緩動動畫,將整個大盒子的寬度等於0,因此出現一個向右的效果

示例代碼: [27-右下角關閉廣告案例.html]

<!-- 樣式部分 -->
<style>
    #box {
        width: 213px;
        position: fixed;
        bottom: 0;
        right: 0;
        overflow: hidden;
    }
    
    #close {
        position: absolute;
        top: 0;
        right: 0;
        width: 30px;
        height: 30px;
        cursor: pointer;
        color: #FFFFFF;
        text-align: center;
    }
    
    .img {
        display: block;
        width: 212px;
        z-index: 99;
    }
</style>

<!-- html 部分 -->
<div id="box">
    <div id="hd">
        <span id="close"> x </span>
        <img src="../image/關閉廣告/banna_up.png" class="img" alt="" />
    </div>
    <div id="bt">
        <img src="../image/關閉廣告/banner_down.png" class="img" alt="" />
    </div>
</div>

<!-- js 部分 -->
<script src="../js/slow-animate-styles.js"></script>
<script>
    var close = document.getElementById('close');
    var box = document.getElementById('box');
    var bt = document.getElementById('bt');

    close.onclick = function() {
        slowAnimateStyles(bt, {
            height: 0
        }, function() {
            slowAnimateStyles(box, {
                width: 0
            });
        });
    }
</script>

效果圖:

image

7. 手風琴案例

手風琴效果在網頁中用的也特別的多,下面咱們會介紹兩種實現的方法,固然我的比較偏好第二種。

一、浮動版手風琴

實現原理:

  • ul,li進行佈局,li左浮動,而且設置等分的寬度;
  • 給每一個li註冊鼠標通過事件,當鼠標通過的時候利用排他原理,將全部的li寬度設置成最小寬度,將當前通過的li寬度設置一個最大寬度;
  • 而後再去設置鼠標離開事件,當鼠標離開時讓全部的li再恢復到等分的寬度。

示例代碼: [28-手風琴-浮動版.html]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
        list-style: none;
    }
    #box {
        width: 900px;
        height: 441px;
        margin: 100px auto;
        overflow: hidden;
        border-radius: 30px;
    }
    ul {
        /* ul的寬要比外面的盒子大一點,不然在添加動畫效果的時候,最後一個li會出現閃動 */
        width: 120%;
        height: 100%;
        overflow: hidden;
    }
    li {
        width: 180px;
        height: 100%;
        float: left;
    }
</style>

<!-- html 部分 -->
<div id="box">
    <ul>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
    </ul>
</div>

<!-- js 部分 -->
<script src="../js/slow-animate-styles.js"></script>
<script>
    var box = document.getElementById('box');
    var lis = box.getElementsByTagName("li");

    for (var i = 0; i < lis.length; i++) {
        // 動態建立img標籤
        var img = document.createElement("img");
        img.src = "../image/手風琴/" + (i + 1) + ".png";
        lis[i].appendChild(img);
        // 給全部li註冊鼠標通過事件,讓當前的li寬度變成 500,其他的li寬度變成100
        lis[i].onmouseover = function() {
            for (var i = 0; i < lis.length; i++) {
                // 先讓全部的li寬度變成100
                slowAnimateStyles(lis[i], {
                    width: 100
                });
                // 鼠標當前通過的寬度爲500
                slowAnimateStyles(this, {
                    width: 500
                })
            }
        };
        // 當鼠標離開的時候,因此的li 寬度恢復到180px
        lis[i].onmouseout = function() {
            for (var i = 0; i < lis.length; i++) {
                slowAnimateStyles(lis[i], {
                    width: 180
                })
            }
        }
    }
</script>

效果圖:

image

二、定位版手風琴

實現原理:

  • 給外部大盒子設置一個與圖片大小一致的寬高,而且設置相對定位
  • 仍是採用ul,li結構,li設置寬高,與圖片大小一致,設置絕對定
  • 動態的給li添加背景圖片,由於li絕對定位的緣由,此時全部的li都疊在一塊兒
  • 動態的給每一個li設置left值(left*i),這時候li就會依次排開
  • 大盒子還要設置一個overflow-hidden屬性,將多餘的隱藏掉
  • 給每一個li註冊鼠標鼠標通過事件,而後根據下面推算出的規律(當前鼠標通過的索引index,他以前包括他本身的left值都是,設定的最小值乘以對應的索引。而他後面的會將設定的最小值乘以對應的索引後再加上450,這裏的450不是一個固定值,根據規律找出來的)進行判斷,設置各自的left值;
  • 鼠標離開的時候再讓全部的盒子恢復到一開始的位置,每一個li顯示等分的寬度

大盒子沒有overflow-hidden的時候:

image

畫個圖,理解一下:

image

找規律:

結合上面的圖片,咱們能夠找到一個規律
  • 當鼠標在第1個li上的時候,li下標index爲0:

    • index:0 left:0
    • index:1 left:500px
    • index:2 left:550px
    • index:3 left:600px
    • index:4 left:650px
  • 當鼠標在第2個li上的時候,li下標index爲1:

    • index:0 left:0
    • index:1 left:50px
    • index:2 left:550px
    • index:3 left:600px
    • index:4 left:650px
  • 當鼠標在第3個li上的時候,li下標index爲2:

    • index:0 left:0
    • index:1 left:50px
    • index:2 left:100px
    • index:3 left:600px
    • index:4 left:650px

看出規律了嗎?

  • 當對應li的下標<=鼠標懸停的的下標上的時候left值 是50*i
  • 當對應li的下標>鼠標懸停的的下標上的時候left值 是50*i + ,450(450不是固定的值,是通過計算出來的)

示例代碼: 29-手風琴-定位版.html]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
        list-style: none;
    }
    
    #box {
        width: 700px;
        height: 440px;
        margin: 100px auto;
        position: relative;
        overflow: hidden;
        box-sizing: border-box;
        border-radius: 30px;
    }
    
    li {
        width: 700px;
        height: 440px;
        position: absolute;
        /* background: yellow; */
    }
</style>

<!-- html 部分 -->
<div id="box">
    <ul>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
    </ul>
</div>

<!-- js 部分 -->
<script src="../js/slow-animate-styles.js"></script>
<script>
    var box = document.getElementById('box');
    var lis = box.getElementsByTagName('li');


    for (var i = 0; i < lis.length; i++) {
        lis[i].index = i;
        // 動態添加li的背景圖片 由於i下標從0開始,可是圖片序號是從1開始 因此jia1
        lis[i].style.backgroundImage = "url(../image/手風琴/" + (i + 1) + ".png)";
        // 如今都疊在一塊兒,設置left 讓他們分開來 700/5 ==> 140px
        lis[i].style.left = 140 * i + "px";

        // 註冊鼠標通過事件,讓當前的顯示寬度爲500,其他的爲50
        lis[i].onmouseover = function() {
            for (var i = 0; i < lis.length; i++) {
                // 判斷當i小於等於當前鼠標停留的下標的時候,給li的left設置 50*i
                if (i <= this.index) {
                    slowAnimateStyles(lis[i], {
                        left: 50 * i
                    });
                    // 當i大於當前鼠標停留的索引的時候,給後邊的li的left設置 50*i + 450 
                } else {
                    slowAnimateStyles(lis[i], {
                        left: 50 * i + 450
                    });
                }
            }
        }

        // 註冊鼠標離開事件,讓全部的li都恢復到最初的樣式
        lis[i].onmouseout = function() {
            for (var i = 0; i < lis.length; i++) {
                slowAnimateStyles(lis[i], {
                    left: 140 * i
                });
            }
        }
    }
</script>

效果圖:

image

8.旋轉木馬案例

旋轉木馬也叫旋轉輪播圖,在效果上它就是旋轉版的輪播圖,可是在實現原理上卻一點一不同

旋轉木馬原理:

  • 利用ulli方式將圖片包裹在li裏,而且對每一個li的大小、層級、不透明度以及定位的位置設置好
  • 樣式上可能比較繁瑣,咱們將上面的每一個參數再以對象的方式存到數組datas
  • 以前封裝過一個緩動動畫函數,能夠改變層級和不透明度,這裏正好用獲得
  • 其實拋開上面樣式上的細節,旋轉木馬最核心的就是運用到幾個數組經常使用的方法 popunshiftshiftpush
  • 點擊右按鈕的時候,將datas裏的最後一項利用pop刪除掉,而且返回這個刪除的數據,再將這個數據unshift到數組的最前面。從新遍歷數組,執行一遍動畫
  • 點擊左箭頭的時候,將datas裏的最前面一項利用shift刪除掉,而且返回這個刪除的數據,再將這個數據push到數組的最後面。從新遍歷數組,執行一遍動畫
  • 再給按鈕添加一個節流閥,沒加以前不停地點擊按鈕,圖片就會不停切換,加上以後,點一次執行完才能夠再次點擊。

示例代碼: [30-旋轉木馬輪播圖案例.html]

<!-- 樣式部分 -->
<style>
    * {
        margin: 0;
        padding: 0;
        list-style: none;
    }
    body {
        background: #666;
    }
    .wrap {
        width: 1200px;
        margin: 200px auto;
    }
    .slide {
        height: 340px;
        position: relative;
    }
    .slide li {
        position: absolute;
        left: 300px;
        top: 0;
    }
    img {
        width: 100%;
    }
    .arrow {
        opacity: 0;
        position: relative;
        z-index: 99;
        top: 50%;
    }
    .arrow #left,
    .arrow #right {
        width: 40px;
        height: 90px;
        position: absolute;
        top: 50%;
        margin-top: -45px;
        background: url(../image/旋轉木馬/left.png);
        background-size: cover;
        z-index: 99;
    }
    .arrow #right {
        right: 0;
        background: url(../image/旋轉木馬/right.png);
        background-size: cover;
    }
</style>

<!-- html 部分 -->
<div class="wrap" id="wrap">
    <div class="slide" id="slide">
        <ul>
            <li><img src="../image/1.jpg" alt=""></li>
            <li><img src="../image/2.jpg" alt=""></li>
            <li><img src="../image/3.jpg" alt=""></li>
            <li><img src="../image/4.jpg" alt=""></li>
            <li><img src="../image/5.jpg" alt=""></li>
        </ul>
        <div class="arrow" id="arrow">
            <a href="javascript:;"><span id="left"></span></a>
            <a href="javascript:;"><span id="right"></span></a>
        </div>
    </div>
</div>

<!-- js 部分 -->
<script src="../js/slow-animate-styles.js">
</script>
<script>
    // 將其他四張位置與透明度等信息,存放在一個數組中
    var datas = [{
            "width": 300,
            "top": -20,
            "left": 150,
            "opacity": 20,
            "zIndex": 2
        }, //0
        {
            "width": 500,
            "top": 30,
            "left": 50,
            "opacity": 80,
            "zIndex": 3
        }, //1
        {
            "width": 600,
            "top": 100,
            "left": 300,
            "opacity": 100,
            "zIndex": 4
        }, //2
        {
            "width": 500,
            "top": 30,
            "left": 650,
            "opacity": 80,
            "zIndex": 3
        }, //3
        {
            "width": 300,
            "top": -20,
            "left": 750,
            "opacity": 20,
            "zIndex": 2
        } //4
    ];

    var slide = document.getElementById('slide');
    var lis = slide.getElementsByTagName('li');
    var arrow = document.getElementById('arrow');
    var left = document.getElementById('left');
    var right = document.getElementById('right');

    // 定義一個節流閥
    var flag = true;

    // 一開始頁面刷新的時候,將datas裏的數據 動態添加進去
    for (var i = 0; i < lis.length; i++) {
        slowAnimateStyles(lis[i], datas[i]);
    };

    // 鼠標通過的時候 箭頭顯示
    slide.onmouseover = function() {
        slowAnimateStyles(arrow, {
            opacity: 100
        })
    };

    // 鼠標離開的時候 箭頭隱藏
    slide.onmouseout = function() {
        slowAnimateStyles(arrow, {
            opacity: 0
        })
    };

    // 點擊右箭頭的時候
    // 利用數組的pop 和 unshift方法對數組datas進行操做
    // pop 會刪除數組的最後一項,而且返回這一項。 unshift 會在數組的最前添加
    right.onclick = function() {
        // 只有節流閥爲true的時候 點擊纔會執行裏面的代碼
        if (flag) {
            // 電擊後一進來就將節流閥關上,再次點擊的時候就不會進來
            flag = false;
            datas.unshift(datas.pop());
            for (var i = 0; i < lis.length; i++) {
                // 點擊一次就要動畫渲染一次,datas[i]  實際上是一個對象
                /*
                    {
                        "width": 300,
                        "top": -20,
                        "left": 150,
                        "opacity": 20,
                        "zIndex": 2
                    }
                */
                slowAnimateStyles(lis[i], datas[i], function() {
                    // 當動畫執行完,也就是回調函數觸發的時候,再將節流閥打開,這樣就能夠繼續點擊了
                    flag = true;
                });
            }
        }
    }

    // 點擊左箭頭
    // 利用數組的 shift 和 push方法對數組datas進行操做
    // shift 會刪除數組的第一項,而且返回這一項。 push 會在數組的最後添加
    left.onclick = function() {
        if (flag) {
            flag = false;
            datas.push(datas.shift());
            for (var i = 0; i < lis.length; i++) {
                slowAnimateStyles(lis[i], datas[i], function() {
                    flag = true;
                });
            }
        }

    }
</script>

效果圖:

image

上一篇:JavaScript 基礎知識 - BOM篇
下一篇:JavaScript 進階知識 - 特效篇(二)

相關文章
相關標籤/搜索