06-JS特效-02動(dòng)畫&冒泡

動(dòng)畫

  • 定義:運(yùn)動(dòng)的圖片(讓圖片或者圖畫動(dòng)起來)。
  • 原理:未來位置等于當(dāng)前位置加上步長(zhǎng)(盒子的位置 = 盒子本身所在位置 + 步長(zhǎng))。
  • 位置獲取和賦值方式:用style.left賦值,用offsetLeft獲取值;style.left獲取值不方便,只能獲取行內(nèi)式,如果沒有就返回"",容易出現(xiàn)NaN,即使有值也是包含px的字符串;offsetLeft獲取值特別方便,而且獲取到的值是現(xiàn)成的number類型,方便計(jì)算,因?yàn)樗侵蛔x的不能賦值,所以一般用style.left賦值。

動(dòng)畫的種類

閃現(xiàn)(很少使用)
<body>
<button>閃動(dòng)</button>
<div></div>

<script src="../05-jQuery/jquery-1.11.1.js"></script>
<script type="text/javascript">
    var btn = $("button");
    var divs = $("div");

    $(btn[0]).click(function () {
        divs[0].style.left = "500px";
    });
</script>

</body>
勻速(重點(diǎn)
<body>
<button>勻動(dòng)到200</button>
<button>勻動(dòng)到400</button>
<div></div>

<script>
    var btn200 = document.getElementsByTagName("button")[0];
    var btn400 = document.getElementsByTagName("button")[1];
    var div = document.getElementsByTagName("div")[0];
    var timer = null;
    btn200.onclick = function () {
        animation(200);
    };
    btn400.onclick = function () {
        animation(400);
    };

    function animation(distance) {
        timer = setInterval(function () {
            div.style.left = div.offsetLeft + 10 + "px";
            if (div.offsetLeftd == distance){
                clearInterval(timer);
            }
        },30);
    }
</script>
</body>
  • 系列bug:
  • 定時(shí)器:多次使用定時(shí)器之后,動(dòng)畫會(huì)越來越快:
    • 解決方案:在使用定時(shí)器之前,先清除定時(shí)器。
  • 不能返回:
    • 解決方案:判斷目標(biāo)位置和當(dāng)前位置的大小,如果目標(biāo)位置大,步進(jìn)就為正,否則就為負(fù)。
  • 如果目標(biāo)位置和當(dāng)前位置的距離小于步進(jìn),就不再進(jìn)行運(yùn)動(dòng),但是會(huì)出現(xiàn)抖動(dòng)現(xiàn)象:
    • 解決方案:直接令當(dāng)前位置等于目標(biāo)位置。
  • 一個(gè)盒子只能有一個(gè)定時(shí)器,這樣的話,不會(huì)出現(xiàn)定時(shí)器沖突;
  • 將定時(shí)器本身綁定成為盒子的一個(gè)屬性,不會(huì)和其它盒子定時(shí)器產(chǎn)生沖突
<head>
    <meta charset="UTF-8">
    <title>勻速運(yùn)動(dòng)</title>

    <style type="text/css">
        body{
            margin:0;
            padding:0;
        }
        .box1{
            margin:0;
            position: relative;
        }
        .box2{
            width: 100px;
            height: 100px;
            background-color: pink;
            position: absolute;
        }
        .box3{
            width: 100px;
            height: 100px;
            background-color: red;
            position: absolute;
            margin-top: 130px;
        }
    </style>
</head>

<body>
<div class="box1">
    <!--勻速運(yùn)動(dòng)-->
    <button>勻動(dòng)到200</button>
    <button style="left: 50px">勻動(dòng)到400</button>
    <div class="box2"></div>
    <div class="box3"></div>
</div>

<script>
    var btn200 = document.getElementsByTagName("button")[0];
    var btn400 = document.getElementsByTagName("button")[1];
    var box2 = document.getElementsByTagName("div")[1];
    var box3 = document.getElementsByClassName("box3")[0];
    btn200.onclick = function () {
        animation(box2,200);
    };
    btn400.onclick = function () {
        animation(box3,400);
    };

    function animation(ele,distance) {
        //1.使用定時(shí)器之前必須先清除,排除多次點(diǎn)擊越來越快的bug
        clearInterval(ele.timer);   //將定時(shí)器作為元素的屬性,這樣一個(gè)盒子綁定一個(gè)自己的定時(shí)器,排除了其它盒子定時(shí)器的干擾
        //2.為了能讓div可以從后往前走,要先進(jìn)行判斷目標(biāo)位置和當(dāng)前位置的大小,如果目標(biāo)位置大,步進(jìn)就正,否則就負(fù)
        var speed = distance>box2.offsetLeft?10:(-10);
        ele.timer = setInterval(function () {
            var val = distance - ele.offsetLeft;   //此句位置至關(guān)重要,不能和下一句倒換位置,必須要先獲取目標(biāo)位置和當(dāng)前位置的距離
            ele.style.left = ele.offsetLeft + speed + "px";   //此時(shí)盒子會(huì)進(jìn)行運(yùn)動(dòng)

            //3.到達(dá)目標(biāo)位置后,再次點(diǎn)擊會(huì)出現(xiàn)抖動(dòng)現(xiàn)象。解決方案,判斷目標(biāo)位置和當(dāng)前位置距離是否小于步進(jìn),如果小于就讓兩者相等,并立刻清除定時(shí)器
            if (Math.abs(val)<Math.abs(speed)){
                ele.style.left = distance + "px";
                clearInterval(ele.timer);
            }
        },30);
    }
</script>
</body>
  • 練習(xí):
  • 滑動(dòng)輪播;
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>練習(xí)</title>

    <style type="text/css">
        *{
            margin: 0;
            padding: 0;
        }

        .box{
            border: 1px solid #999;
            width: 490px;
            height: 170px;
            margin: 60px auto 0;
            padding:5px;
        }
        .inner{
            position: relative;
            width: 490px;
            height: 179px;
            overflow: hidden;
        }
        ul{
            width: 500%;
            position: absolute;
        }
        li{
            list-style: none;
            float: left;
        }

        .square{
            position: absolute;
            right: 10px;
            top: 140px;
        }
        .square span{
            display: inline-block;
            width: 18px;
            height: 18px;
            background-color: #aaa;
            text-align: center;
            line-height: 18px;
            cursor: pointer;
        }

        .inner .current{
            background-color: darkorange;
        }
    </style>

</head>

<body>

<div class="box">
    <div class="inner">
        <ul>
            <li>![](images/01.jpg)</li>
            <li>![](images/02.jpg)</li>
            <li>![](images/03.jpg)</li>
            <li>![](images/04.jpg)</li>
            <li>![](images/05.jpg)</li>
        </ul>

        <div class="square">
            <span class="current">1</span>
            <span>2</span>
            <span>3</span>
            <span>4</span>
            <span>5</span>
        </div>
    </div>
</div>

<script>
    window.onload = function () {
        //獲取事件源和相關(guān)元素
        var inner = document.getElementsByClassName("inner")[0];
        var spans = inner.children[1].children;
        var ul = inner.children[0];
        //獲取窗口寬度
        var scrollWidth = inner.offsetWidth;
        for(var i=0; i<spans.length; i++){
            spans[i].index = i;
            spans[i].onmouseover = function () {
                //使用排它思想點(diǎn)亮盒子
                for(var j=0; j<spans.length; j++){
                    spans[j].className = "";
                }
                this.className = "current";
                //移動(dòng)盒子
                console.log(ul);
                animation(ul,-this.index*scrollWidth);

            }
        }

        function animation(ele,target) {
            clearInterval(ele.timer);
            var speed = target>ele.offsetLeft ? 10:-10;
            ele.timer = setInterval(function () {
                var val = target - ele.offsetLeft;
                ele.style.left = ele.offsetLeft + speed + "px";

                if (Math.abs(val)<Math.abs(speed)){
                    ele.style.left = target + "px";
                    clearInterval(ele.timer);
                }
            },1);
        }
    };

</script>

</body>
</html>
  • 左右焦點(diǎn)圖;
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>練習(xí)</title>

    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .box{
            width: 490px;
            height: 170px;
            margin: 60px auto;
            border: 1px solid #aaa;
            padding: 5px;
        }
        .inner{
            width: 490px;
            height: 170px;
            position: relative;
            overflow: hidden;
        }
        ul{
            width: 500%;
            position: absolute;
        }
        li{
            list-style: none;
            float: left;
        }

        .rl{
            display: none;
        }
        .rl span{
            position: absolute;
            width: 30px;
            height: 30px;
            top: 70px;
            color: #fff;
            font-size: 30px;
            line-height: 30px;
            text-align: center;
            background: #000 ;
            left: 10px;
            cursor: pointer;
            opacity: 0.7;
        }
        .rl .right{
            right: 10px;
            left: auto;
        }
    </style>

</head>

<body>

<div class="box">
    <div class="inner">
        <ul>
            <li>![](images/01.jpg)</li>
            <li>![](images/02.jpg)</li>
            <li>![](images/03.jpg)</li>
            <li>![](images/04.jpg)</li>
            <li>![](images/05.jpg)</li>
        </ul>
        <div class="rl">
            <span class="left"><</span>
            <span class="right">></span>
        </div>
    </div>
</div>

<script>
    window.onload = function () {
        //1.獲取事件源及相關(guān)元素
        var box = document.getElementsByClassName("box")[0];
        var inner = document.getElementsByClassName("inner")[0];
        var ul = document.getElementsByTagName("ul")[0];
        var rl = document.getElementsByClassName("rl")[0];
        var left = document.getElementsByClassName("left")[0];
        var right = document.getElementsByClassName("right")[0];
        //獲取窗口寬度
        var scrollW = inner.offsetWidth;

        //2.左右按鈕的顯示和隱藏
        box.onmouseover = function () {
            rl.style.display = "block";
        };
        box.onmouseout = function () {
            rl.style.display = "none";
        };

        //3.點(diǎn)擊左右按鈕
        //定義一個(gè)計(jì)數(shù)器
        var index = 0;
        //點(diǎn)擊左邊按鈕
        left.onclick = function () {
            index --;
            //如果現(xiàn)實(shí)第一個(gè)圖片時(shí),再點(diǎn)擊左邊按鈕時(shí)仍讓其顯示第一個(gè)圖片
            if (index < 0){
                index = 0;
            }
            animation(ul,- index * scrollW);
        };
        //點(diǎn)擊右邊按鈕
        right.onclick = function () {
            index ++;
            //如果當(dāng)前圖片已經(jīng)是最后一個(gè),再點(diǎn)擊右邊按鈕時(shí)始終讓它展示最后一個(gè)
            if (index > ul.children.length - 1){
                index = ul.children.length - 1;
            }
            animation(ul,- index * scrollW)
        };

        //動(dòng)畫封裝
        function animation(ele,target) {
            //清除定時(shí)器
            clearInterval(ele.timer);
            //設(shè)置步進(jìn)大小
            var speed = target>ele.offsetLeft ? 10:-10;
            //設(shè)置定時(shí)器
            ele.timer = setInterval(function () {
                //先獲取元素目標(biāo)位置和當(dāng)前位置的距離val
                var val = target - ele.offsetLeft;
                ele.style.left = ele.offsetLeft + speed + "px";
                //如果val小于步進(jìn),就使元素位置等于目標(biāo)位置,并清除定時(shí)器
                if (Math.abs(val) < Math.abs(speed)){
                    ele.style.left = target + "px";
                    clearInterval(ele.timer);
                }
            },10);
        }
    }

</script>

</body>
</html>
  • 帶有定時(shí)器的輪播圖。
緩動(dòng)(重點(diǎn)
  • 現(xiàn)象:開始走很快,越走越慢,步長(zhǎng)越來越小,類似電梯停止。

  • 好處:

  • 有非常逼真的緩動(dòng)效果,實(shí)現(xiàn)的動(dòng)畫效果更細(xì)膩;

  • 如果不清除定時(shí)器,物體永遠(yuǎn)跟著目標(biāo)在移動(dòng)。

  • 緩動(dòng)動(dòng)畫公式:

  • 動(dòng)畫原理 = 盒子位置 + 步長(zhǎng)(步長(zhǎng)越來越?。?;

  • 盒子位置 = 盒子本身位置+(目標(biāo)位置-盒子本身位置)/ 10;
    公式:position = position + (target - position) / 10;

<body>
    <button class="animate1">緩動(dòng)到400</button>
    <div class="huanDong"></div>

    <script>
        var btn = document.getElementsByClassName("animate1")[0];
        var div1 = document.getElementsByClassName("huanDong")[0];

        btn.onclick = function () {
            var timer = setInterval(function () {
                div1.style.left = div1.offsetLeft + (400 - div1.offsetLeft) / 10 + "px";
                if (div1.offsetLeft == 400){
                    clearInterval(timer);
                }
            },30);
        };

    </script>
</body>
  • 但是有個(gè)現(xiàn)象,動(dòng)畫會(huì)永遠(yuǎn)執(zhí)行下去,不會(huì)停止。舉個(gè)例子:把一桶水,倒進(jìn)另一通,每次倒一半,永遠(yuǎn)倒不完;還有個(gè)原因就是offsetLeft會(huì)將獲取值四舍五入之后再進(jìn)行計(jì)算。

  • 疑問:

  • 原理:數(shù)學(xué)算法。其他運(yùn)動(dòng)形式其他算法。

  • 為什么是10?:更符合我們的計(jì)算,邏輯,審美,人體工程學(xué)。

  • (實(shí)際工作中,領(lǐng)導(dǎo)給我們什么技術(shù),我們就直接用,空余時(shí)間研究源碼和原理)。

  • 動(dòng)畫封裝問題:

  • 小數(shù)只能給定樣式,最好不要參與計(jì)算。(offsetLeft會(huì)將獲取值四舍五入取整后計(jì)算);

  • 如果正向運(yùn)動(dòng),步長(zhǎng)是大于0;如果反向運(yùn)動(dòng),步長(zhǎng)就小于0。大于0向上取整,小于0向下取整。大于0的時(shí)候向上取整能夠取到1,小于零時(shí)向下取整才能取到-1(會(huì)在9和-9距離的時(shí)候出現(xiàn)問題)。

<body>

    <button class="bigger">緩動(dòng)到400</button>
    <button class="smaller">緩動(dòng)到200</button>
    <button class="zero">緩動(dòng)到0</button>
    <div class="huanDong"></div>

    <script>
        var btn1 = document.getElementsByClassName("bigger")[0];
        var btn2 = document.getElementsByClassName("smaller")[0];
        var btn3 = document.getElementsByClassName("zero")[0];
        var div1 = document.getElementsByClassName("huanDong")[0];
        btn1.onclick = function () {
            animation(div1,400);
        };
        btn2.onclick = function () {
            animation(div1,200);
        };
        btn3.onclick = function () {
            animation(div1,0);
        };

        function animation(ele,target) {
            //清除定時(shí)器
            clearInterval(ele.timer);

            ele.timer = setInterval(function () {
                //計(jì)算步長(zhǎng)
                var step = (target - ele.offsetLeft) / 10;
                step = step>0 ? Math.ceil(step):Math.floor(step);
                //移動(dòng)位置
                ele.style.left = ele.offsetLeft + step + "px";
                //如果距離小于步長(zhǎng)就將目標(biāo)位置賦值給盒子,并清除定時(shí)器
                if (Math.abs(target - ele.offsetLeft)<=Math.abs(step)){
                    ele.style.left = target + "px";
                    clearInterval(ele.timer);
                }
            },30);
        }

    </script>
</body>
  • 案例:
  • 筋斗云(光標(biāo)移動(dòng)到對(duì)應(yīng)標(biāo)簽時(shí),筋斗云圖片也移動(dòng)到對(duì)應(yīng)位置,在沒有點(diǎn)擊的情況下如果移開光標(biāo)筋斗云到最初位置;如果點(diǎn)擊了某個(gè)標(biāo)簽,移開光標(biāo)時(shí),筋斗云仍然停留在點(diǎn)擊的標(biāo)簽處)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>筋斗云練習(xí)</title>

    <style>
        *{
            margin: 0;
            padding: 0;
        }

        body{
            background-color: rgba(0,0,0,0.2);
        }

        .tab{
            width: 600px;
            height: 42px;
            border-radius: 8px;
            background: #fff url("images/wifi.png") right center no-repeat;
            margin: 160px auto;
            position: relative;
        }
        ul{
            list-style: none;
            position: absolute;
        }
        li{
            float: left;
            width: 83px;
            height: 42px;
            text-align: center;
            line-height: 42px;
            cursor: pointer;
        }
        span[class=cloud]{
            position: absolute;
            width: 83px;
            height: 42px;
            background: url("images/cloud.gif");
        }
    </style>
</head>

<body>
    <div class="tab">
        <span class="cloud"></span>
        <ul>
            <li>家鄉(xiāng)文化</li>
            <li>沿海風(fēng)光</li>
            <li>文人騷客</li>
            <li>特色景點(diǎn)</li>
            <li>特產(chǎn)美食</li>
        </ul>
    </div>

    <script>
        //入口函數(shù)
        window.onload = function () {

            //獲取事件源和一些相關(guān)的元素
            var tab = document.getElementsByClassName("tab")[0];
            var lis = document.getElementsByTagName("li");
            var liW = lis[0].offsetWidth;
            var cloud = document.getElementsByClassName("cloud")[0];

            //書寫事件代碼
            //定義一個(gè)計(jì)數(shù)器,移除光標(biāo)和點(diǎn)擊光標(biāo)時(shí)會(huì)用到
            var count = 0;

            for(var i=0; i<lis.length; i++){
                lis[i].index = i;
                //綁定鼠標(biāo)進(jìn)入事件
                lis[i].onmouseover = function () {
                    //cloudAnimate(cloud,this.index * liW);
                    cloudAnimate(cloud,this.offsetLeft);
                };

                //鼠標(biāo)點(diǎn)擊事件
                lis[i].onclick = function () {
                    count = this.index;
                    cloudAnimate(cloud,count * liW);
                }
            }

            //鼠標(biāo)離開事件
            tab.onmouseout = function () {
                cloudAnimate(cloud,count * liW);
            };


            //定義動(dòng)畫函數(shù)
            function cloudAnimate(ele,target) {
                //清空定時(shí)器
                clearInterval(ele.timer);
                //設(shè)置定時(shí)器
                ele.timer = setInterval(function () {
                    //計(jì)算步長(zhǎng)
                    var step = (target - ele.offsetLeft) / 10;
                    step = step>0 ? Math.ceil(step):Math.floor(step);
                    //動(dòng)畫原理
                    ele.style.left = ele.offsetLeft + step + "px";
                    //如果距離小于等于步長(zhǎng),就將目標(biāo)位置直接賦值,并清除定時(shí)器
                    if (Math.abs(target - ele.offsetLeft) <= Math.abs(step)){
                        ele.style.left = target + "px";
                        clearInterval(ele.timer);
                    }
                },10);
            }
        };
    </script>
</body>
</html>

冒泡

  • 事件冒泡: 當(dāng)一個(gè)元素上的事件被觸發(fā)的時(shí)候,比如說鼠標(biāo)點(diǎn)擊了一個(gè)按鈕,同樣的事件將會(huì)在那個(gè)元素的所有祖先元素中被觸發(fā)。這一過程被稱為事件冒泡;這個(gè)事件從原始元素開始一直冒泡到DOM樹的最上層。(BUG)。(好比如:本來應(yīng)該一人做事一人當(dāng),結(jié)果,我做錯(cuò)了事情,你去告訴我媽)

  • 事件傳播階段:

  • 事件傳播的三個(gè)階段:捕獲、冒泡、目標(biāo)階段;

  • 事件捕獲階段:事件從最上一級(jí)標(biāo)簽開始往下查找,直到捕獲到事件目標(biāo)(target)【從上往下】;

  • 事件冒泡階段:事件從事件目標(biāo)開始,往上冒泡直到頁面的最上一級(jí)標(biāo)簽【從下往上】。

  • 什么是冒泡:子元素事件被觸動(dòng),父盒子的同樣的事件也會(huì)被觸動(dòng)。

  • 冒泡順序:

  • IE6.0:div->body->html->document;

  • 其它瀏覽器:div->body->html->document->window。

  • 不是所有的事件都能冒泡,以下事件不能冒泡:blur、focus、load、unload、onmouseenter、onmouseleave。

  • 取消冒泡就是取消這種機(jī)制。

阻止冒泡
  • w3c的方法是:
  • 火狐、谷歌、IE11:event.stopPropagation();
  • IE10以下:event.cancelBubble = true;
  • 兼容代碼如下:
var event = event || window.event;
if(event && event.stopPropagation){
    event.stopPropagation();
}else{
    event.cancelBubble = true;
}
獲取當(dāng)前對(duì)象
  • IE678:event.srcElement(事件源);
  • 火狐、谷歌等:event.target(事件源);,誰觸動(dòng)事件,誰就會(huì)被放進(jìn)target中;
  • 兼容寫法獲取元素ID:
var event = event || window.event;
var targetId = event.target ? event.target.id:event.srcElement.id;

addEventListenner(參數(shù)1,參數(shù)2,參數(shù)3)

  • 調(diào)用者是:事件源;

  • 參數(shù)1:事件去掉on;

  • 參數(shù)2 :調(diào)用的函數(shù)

  • 參數(shù)3:可有可無。默認(rèn)false,false情況下,支持冒泡,true支持捕獲。

  • 案例

  • 點(diǎn)擊空白隱藏模態(tài)框

    • Document事件的綁定,無論綁定什么事件,只要事件被出發(fā),傳遞過來的應(yīng)該是指定的元素本身,而不是document。
  • 事件委托

    • 先綁定,后創(chuàng)建的元素(沒有事件);
    • 組織冒泡,組織自己向父系盒子冒泡,所有的泡冒泡到組織的位置停止向上冒泡;
    • event.target是在事件被觸動(dòng)的時(shí)候把事件源綁定到event的target屬性中。而之前的target是我們自定義的一個(gè)變量觸動(dòng)。

補(bǔ)充

獲取行內(nèi)式和內(nèi)嵌式屬性的值

  • 行內(nèi)式:div.style.width,只能獲取行內(nèi)式屬性的值,有單位px,只包含盒子的width,不包含padding和border;

  • 內(nèi)嵌式:div.offsetWidth,沒有單位,包含width、padding、border。

  • 獲取行內(nèi)式和內(nèi)嵌式屬性的值:

  • window.getComputedStyle(div,null)獲取到的值是對(duì)象類型:

var w = window.getComputedStyle(div,null).width;
var color = window.getComputedStyle(div, null)["backgroundColor"];
- 獲取到的值有單位`px`,只包含`width`,不包含padding和border;
  • div.currentStyle.width只支持IE678:
console.log(div.currentStyle.width);
console.log(div.currentStyle.padding);
  • 兼容方式獲取元素某個(gè)樣式
function getStyle(ele, attr) {
    if(window.getComputedStyle){
        return window.getComputedStyle(ele,null).attr;
    }else {
        return ele.currentStyle[attr];
    }
}

緩動(dòng)的高級(jí)封裝

單個(gè)屬性封裝
  • 利用通過兼容方式獲取元素某個(gè)屬性的值:
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>緩動(dòng)封裝-單個(gè)屬性</title>

    <style>
        div{
            width: 100px;
            height: 100px;
            border: 5px solid #ddd;
            margin-top: 10px;
            background-color: pink;
            position: absolute;
        }
    </style>
</head>
<body>
    <button>緩動(dòng)到400</button>
    <button>長(zhǎng)度變成300</button>
    <div></div>

    <script>
        //入口函數(shù)
        window.onload = function () {
            //獲取事件源
            var btns = document.getElementsByTagName("button");
            var area = document.getElementsByTagName("div")[0];
            //綁定事件
            btns[0].onclick = function () {
                animate(area,"left",400);
            };
            btns[1].onclick = function () {
                animate(area,"width",300);
            };

            //緩動(dòng)動(dòng)畫封裝
            function animate(ele,attr,target) {

                //獲取元素屬性的值
                var currentP = parseInt(getStyle(ele,attr)) || 200;

                //清除定時(shí)器
                clearInterval(ele.timer);
                //設(shè)置定時(shí)器
                ele.timer = setInterval(function () {
                    //計(jì)算步長(zhǎng)
                    var step = (target - currentP) / 10;
                    step = step>0 ? Math.ceil(step):Math.floor(step);
                    //動(dòng)畫原理
                    currentP = currentP + step;
                    ele.style[attr] = currentP + "px";
                    //清除定時(shí)器
                    if (Math.abs(target - currentP) <= Math.abs(step)){
                        ele.style[attr] = target + "px";
                        clearInterval(ele.timer);
                    }
                },30);
            }

            //獲取某個(gè)屬性的值
            function getStyle(ele, attr) {
                if (window.getComputedStyle){
                    return window.getComputedStyle(ele,null)[attr];
                }else {
                    return ele.currentStyle[attr];
                }
            }
        }
    </script>
</body>
</html>
多個(gè)屬性封裝
  • 單次運(yùn)動(dòng):
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>多個(gè)屬性</title>

    <style>
        div{
            width: 100px;
            height: 100px;
            position: absolute;
            right: 0;
            background-color: palevioletred;
            border: 5px solid #ccc;
        }
    </style>
</head>
<body>
    <button>點(diǎn)擊運(yùn)動(dòng)</button>
    <div></div>

    <script>
        window.onload = function () {
            //獲取事件源和相關(guān)元素
            var btn = document.getElementsByTagName("button")[0];
            var div = document.getElementsByTagName("div")[0];

            //綁定事件
            btn.onclick = function () {
                //通過JSON修改盒子多個(gè)屬性的值
                var json = {"left":200,"width":200,"height":200,"top":50};
                animate(div,json);
            };

            //封裝動(dòng)畫
            function animate(ele,json) {
                //清除定時(shí)器
                clearInterval(ele.timer);

                //設(shè)置定時(shí)器
                ele.timer = setInterval(function () {
                    //設(shè)置一個(gè)標(biāo)志,記錄是否符合清除定時(shí)器的條件
                    var bool = true;
                    //遍歷json
                    for (var k in json){
                        //獲取元素屬性值
                        var currentP = parseInt(getStyle(ele,k)) || 200;
                        //計(jì)算步長(zhǎng)
                        var step = (json[k] - currentP) / 10;
                        step = step>0 ? Math.ceil(step):Math.floor(step);
                        currentP = currentP + step;
                        ele.style[k] = currentP + "px";
                        //記錄狀態(tài)
                        if (Math.abs(json[k] - currentP) > Math.abs(step)){
                            bool = false;
                        }
                    }

                    //判斷是否符合清除定時(shí)器的條件
                    if (bool){
                        for (var k in json){
                            ele.style[k] = json[k] + "px";
                        }
                        clearInterval(ele.timer);
                    }
                },30);
            }

            //獲取元素屬性值
            function getStyle(ele, attr) {
                if (window.getComputedStyle){
                    return window.getComputedStyle(ele,null)[attr];
                }else {
                    return ele.currentStyle[attr];
                }
            }
        }
    </script>
</body>
</html>
  • 往返運(yùn)動(dòng):方法一
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>多個(gè)屬性</title>

    <style>
        div{
            width: 100px;
            height: 100px;
            position: absolute;
            left: 30px;
            top: 40px;
            background-color: palevioletred;
            border: 5px solid #ccc;
        }
    </style>
</head>
<body>
    <button>點(diǎn)擊運(yùn)動(dòng)</button>
    <div></div>

    <script>
        window.onload = function () {
            //獲取事件源和相關(guān)元素
            var btn = document.getElementsByTagName("button")[0];
            var div = document.getElementsByTagName("div")[0];

            //綁定事件
            btn.onclick = function () {
                //通過JSON修改盒子多個(gè)屬性的值
                var json1 = {"width":200,"height":200,"left":100,"top":150};
                var json2 = {"width":100,"height":100,"left":30,"top":40};

                animate(div,json1,json2);
            };

            //封裝動(dòng)畫
            function animate(ele,json,fn) {
                //清除定時(shí)器
                clearInterval(ele.timer);

                //設(shè)置定時(shí)器
                ele.timer = setInterval(function () {
                    //設(shè)置一個(gè)標(biāo)志,記錄是否符合清除定時(shí)器的條件
                    var bool = true;
                    //遍歷json
                    for (var k in json){
                        //獲取元素屬性值
                        var currentP = parseInt(getStyle(ele,k)) || 200;
                        //計(jì)算步長(zhǎng)
                        var step = (json[k] - currentP) / 10;
                        step = step>0 ? Math.ceil(step):Math.floor(step);
                        currentP = currentP + step;
                        ele.style[k] = currentP + "px";
                        //記錄狀態(tài)
                        if (Math.abs(json[k] - currentP) > Math.abs(step)){
                            bool = false;
                        }
                    }

                    //判斷是否符合清除定時(shí)器的條件
                    if (bool){
                        for (var k in json){
                            ele.style[k] = json[k] + "px";
                        }
                        clearInterval(ele.timer);
                        if (fn){
                            animate(ele,fn);
                        }
                    }
                },30);
            }

            //獲取元素屬性值
            function getStyle(ele, attr) {
                if (window.getComputedStyle){
                    return window.getComputedStyle(ele,null)[attr];
                }else {
                    return ele.currentStyle[attr];
                }
            }
        }
    </script>
</body>
</html>
  • 往返運(yùn)動(dòng):方法二
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>多個(gè)屬性</title>

    <style>
        div{
            width: 100px;
            height: 100px;
            position: absolute;
            left: 30px;
            top: 40px;
            background-color: palevioletred;
            border: 5px solid #ccc;
        }
    </style>
</head>
<body>
    <button>點(diǎn)擊運(yùn)動(dòng)</button>
    <div></div>

    <script>
        window.onload = function () {
            //獲取事件源和相關(guān)元素
            var btn = document.getElementsByTagName("button")[0];
            var div = document.getElementsByTagName("div")[0];

            //綁定事件
            btn.onclick = function () {
                //通過JSON修改盒子多個(gè)屬性的值
                var json1 = {"width":200,"height":200,"left":100,"top":150};
                var json2 = {"width":100,"height":100,"left":30,"top":40};

//                animate(div,json1,json2);
                animate(div,json1,function () {
                    animate(div,json2,function () {
                        animate(div,json1,function () {
                            animate(div,json2,function () {
                                animate(div,json1);
                            });
                        });
                    });
                });
            };

            //封裝動(dòng)畫
            function animate(ele,json,fn) {
                //清除定時(shí)器
                clearInterval(ele.timer);

                //設(shè)置定時(shí)器
                ele.timer = setInterval(function () {
                    //設(shè)置一個(gè)標(biāo)志,記錄是否符合清除定時(shí)器的條件
                    var bool = true;
                    //遍歷json
                    for (var k in json){
                        //獲取元素屬性值
                        var currentP = parseInt(getStyle(ele,k)) || 200;
                        //計(jì)算步長(zhǎng)
                        var step = (json[k] - currentP) / 10;
                        step = step>0 ? Math.ceil(step):Math.floor(step);
                        currentP = currentP + step;
                        ele.style[k] = currentP + "px";
                        //記錄狀態(tài)
                        if (Math.abs(json[k] - currentP) > Math.abs(step)){
                            bool = false;
                        }
                    }

                    //判斷是否符合清除定時(shí)器的條件
                    if (bool){
                        for (var k in json){
                            ele.style[k] = json[k] + "px";
                        }
                        clearInterval(ele.timer);
                        if (fn){
//                            animate(ele,fn);
                            fn();
                        }
                    }
                },30);
            }

            //獲取元素屬性值
            function getStyle(ele, attr) {
                if (window.getComputedStyle){
                    return window.getComputedStyle(ele,null)[attr];
                }else {
                    return ele.currentStyle[attr];
                }
            }
        }
    </script>
</body>
</html>
案例:
  • 手風(fēng)琴;
  • 360開機(jī)動(dòng)畫。

緩動(dòng)框架存在的問題:

  • 有很多屬性依然無法獲取值和賦值:
  • border-radius:1px 2px 3px 4px;
    • 渲染有問題,可以通過拆解來解決,解決方法如下:
div.style.borderBottomLeftRadius = "30px";
div.style.borderTopRightRadius = "30px";
  • opacity:0.5;(沒有單位的)
    • 火狐、谷歌、IE9+,opacity:0.5;,內(nèi)容一起透明,取值范圍:0~1,但是賦值的時(shí)候一般用百分制,但最后賦值時(shí)候再除以一百,因?yàn)樾?shù)有精度問題,防止中途需要運(yùn)算,使誤差變大;
    • IE678(一般不研究),filter:alpha(opacity=50);,取值范圍:0~100。
  • background:rgba(0,0,0,0.3);
  • z-index:1;...等等。
增加了透明度和層級(jí)之后的封裝
/**
 * Created by YJW on 2017/8/11.
 */

//勻速運(yùn)動(dòng)動(dòng)畫
function conSpeedAnimate(ele,distance) {
    //1.使用定時(shí)器之前必須先清除,排除多次點(diǎn)擊越來越快的bug
    clearInterval(ele.timer);   //將定時(shí)器作為元素的屬性
    //2.為了能讓div可以從后往前走,要先進(jìn)行判斷目標(biāo)位置和當(dāng)前位置的大小,如果目標(biāo)位置大,步進(jìn)就正,否則就負(fù)
    var speed = distance>ele.offsetLeft ? 10:-10;
    ele.timer = setInterval(function () {
        var val = distance - ele.offsetLeft;   //測(cè)試發(fā)現(xiàn):此句位置至關(guān)重要,不能和下一句倒換位置
        ele.style.left = ele.offsetLeft + speed + "px";

        //3.到達(dá)目標(biāo)位置后,再次點(diǎn)擊會(huì)出現(xiàn)抖動(dòng)現(xiàn)象。解決方案,判斷目標(biāo)位置和當(dāng)前位置距離是否小于步進(jìn),如果小于就讓兩者相等,并立刻清除定時(shí)器
        if (Math.abs(val)<Math.abs(speed)){
            ele.style.left = distance + "px";
            clearInterval(ele.timer);
        }
    },10);
}

//緩動(dòng)動(dòng)畫
function slowMovingAnimateX(ele,targetX) {
    //清空定時(shí)器
    clearInterval(ele.timer);
    //設(shè)置定時(shí)器
    ele.timer = setInterval(function () {
        //計(jì)算步長(zhǎng)
        var stepX = (targetX - ele.offsetLeft) / 10;
        stepX = stepX>0 ? Math.ceil(stepX):Math.floor(stepX);
        //動(dòng)畫原理
        ele.style.left = ele.offsetLeft + stepX + "px";
        //如果距離小于等于步長(zhǎng),就將目標(biāo)位置直接賦值,并清除定時(shí)器
        if (Math.abs(targetX - ele.offsetLeft) <= Math.abs(stepX)){
            ele.style.left = targetX + "px";
            clearInterval(ele.timer);
        }
    },10);
}
function slowMovingAnimateY(ele,targetY) {
    //清空定時(shí)器
    clearInterval(ele.timer);
    //設(shè)置定時(shí)器
    ele.timer = setInterval(function () {
        //計(jì)算步長(zhǎng)
        var stepY = (targetY - ele.offsetTop) / 10;
        stepY = stepY>0 ? Math.ceil(stepY):Math.floor(stepY);
        //動(dòng)畫原理
        ele.style.top = ele.offsetTop + stepY + "px";
        //如果距離小于等于步長(zhǎng),就將目標(biāo)位置直接賦值,并清除定時(shí)器
        if (Math.abs(targetY - ele.offsetTop) <= Math.abs(stepY)){
            ele.style.top = targetY + "px";
            clearInterval(ele.timer);
        }
    },10);
}

//對(duì)scroll()函數(shù)進(jìn)行封裝
function scroll(){
    //如果window.pageYOffset存在,返回值為0~無窮大,如果不存在返回undefined。
    if (window.pageYOffset !== undefined){
        var json = {
            "top":window.pageYOffset,
            "left":window.pageXOffset
        }
        return json;
    } else if (document.compatMode === CSS1Compat) {
        return {
            "top":document.documentElement.scrollTop,
            "left":document.documentElement.scrollLeft
        }
    } else {
        return {
            "top":document.body.scrollTop,
            "left":document.body.scrollLeft
        }
    }
}

// function scroll() {
//     return {
//         "top" : window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop,
//         "left" : window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft
//     }
// }

//對(duì)clientWidth進(jìn)行封裝(獲取盒子/body、html可視區(qū)域的寬高)
function client() {
    if (window.innerWidth !== undefined){
        return{
            "width":window.innerWidth,
            "height":window.innerHeight
        }
    }else if (document.compatMode === "CSS1Compat"){
        return{
            "width":document.documentElement.clientWidth,
            "height":document.documentElement.clientHeight
        }
    }else {
        return{
            "width":document.body.clientWidth,
            "height":document.body.clientHeight
        }
    }
}

//盒子的隱藏和顯示
function show(div) {
    div.style.display = "block";
}
function hide(div) {
    div.style.display = "none";
}

//獲取鼠標(biāo)在盒子中的位置
function positionXY(event, div) {
    //獲取鼠標(biāo)的位置
    event = event || window.event;
    var pagex = event.pageX || event.clientX + scroll().left;
    var pagey = event.pageY || event.clientY + scroll().top;
    //獲取鼠標(biāo)在盒子中的位置,并讓鼠標(biāo)處在遮蓋中間位置
    var positionX = pagex - content.offsetLeft;
    var positionY = pagey - content.offsetTop;

    return{
        "positionX":positionX,
        "positionY":positionY
    }
}

//緩動(dòng)動(dòng)畫高級(jí)封裝--單個(gè)屬性
function animate(ele,attr,target) {
    //獲取元素屬性的值
    var currentP = parseInt(getStyle(ele,attr)) || 0;

    //清除定時(shí)器
    clearInterval(ele.timer);
    //設(shè)置定時(shí)器
    ele.timer = setInterval(function () {
        //計(jì)算步長(zhǎng)
        var step = (target - currentP) / 10;
        step = step>0 ? Math.ceil(step):Math.floor(step);
        //動(dòng)畫原理
        currentP = currentP + step;
        ele.style[attr] = currentP + "px";
        //清除定時(shí)器
        if (Math.abs(target - currentP) <= Math.abs(step)){
            ele.style[attr] = target + "px";
            clearInterval(ele.timer);
        }
    },30);
}

//封裝緩動(dòng)動(dòng)畫----設(shè)置多個(gè)屬性
function animate(ele,json,fn) {
    //清除定時(shí)器
    clearInterval(ele.timer);

    //設(shè)置定時(shí)器
    ele.timer = setInterval(function () {
        //設(shè)置一個(gè)標(biāo)志,記錄是否符合清除定時(shí)器的條件
        var bool = true;
        //遍歷json
        for (var k in json){
            //獲取元素屬性值
            var currentP = parseInt(getStyle(ele,k)) || 0;
            //計(jì)算步長(zhǎng)
            var step = (json[k] - currentP) / 10;
            step = step>0 ? Math.ceil(step):Math.floor(step);
            currentP = currentP + step;
            ele.style[k] = currentP + "px";
            //記錄狀態(tài)
            if (Math.abs(json[k] - currentP) > Math.abs(step)){
                bool = false;
            }
        }

        //判斷是否符合清除定時(shí)器的條件
        if (bool){
            for (var k in json){
                ele.style[k] = json[k] + "px";
            }
            clearInterval(ele.timer);
            if (fn){
//                            animate(ele,fn);
                fn();
            }
        }
    },30);
}

//緩動(dòng)動(dòng)畫封裝----增加了透明度和層級(jí)
function animate(ele,json,fn) {
    //清除定時(shí)器
    clearInterval(ele.timer);

    //設(shè)置定時(shí)器
    ele.timer = setInterval(function () {
        //設(shè)置一個(gè)標(biāo)志,記錄是否符合清除定時(shí)器的條件
        var bool = true;
        //遍歷json
        for (var k in json){
            //獲取元素屬性值
            var currentP;
            // 如果是opacity獲取值之后需要進(jìn)行特殊處理
            if (k === "opacity"){
                currentP = getStyle(ele,k)*100 || 10;
                console.log(getStyle(ele,k));
            }else {
                console.log(getStyle(ele, k) + " -- " + k);
                currentP = parseInt(getStyle(ele,k)) || 0;
            }

            //計(jì)算步長(zhǎng)
            var step = (json[k] - currentP) / 10;
            step = step>0 ? Math.ceil(step):Math.floor(step);
            currentP = currentP + step;

            //如果是opacity需要特殊賦值處理
            if (k === "opacity"){
                ele.style[k] = currentP / 100;
                //兼容IE678
                ele.style["filter"] = "alpha(opacity=" + currentP + ")";
            }else if (k === "z-index"){
                ele.style[k] = json[k];
            }else {
                ele.style[k] = currentP + "px";
            }

            //記錄狀態(tài)
            if (Math.abs(json[k] - currentP) > Math.abs(step)){
                bool = false;
            }
        }

        //判斷是否符合清除定時(shí)器的條件
        if (bool){
            for (var k in json){
                ele.style[k] = json[k] + "px";
            }
            clearInterval(ele.timer);
            if (fn){
                fn();
            }
        }
    },30);
}

//兼容方式獲取元素屬性值
function getStyle(ele, attr) {
    if(window.getComputedStyle){
        return window.getComputedStyle(ele,null).attr;
    }else {
        return ele.currentStyle[attr];
    }
}
案例
  • 旋轉(zhuǎn)輪播圖;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • offset家族三大家族和一個(gè)事件對(duì)象三大家族(offset/scroll/client)事件對(duì)象/event ...
    Yuann閱讀 1,087評(píng)論 0 5
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,030評(píng)論 25 709
  • 年少的幼稚,年輕的思緒 曾經(jīng)的魯莽,現(xiàn)在的沉穩(wěn) 遺憾淪為遺忘,成全那句無常 如果你還別來無恙,只怪時(shí)間不夠漫長(zhǎng) 若...
    洪孩兒閱讀 281評(píng)論 3 2
  • #連續(xù)留言31天# 【引子】概念與關(guān)聯(lián)->價(jià)值觀->方法論 1.概念與關(guān)聯(lián)->價(jià)值觀->方法論,改變你“看”到的世...
    毛豆爸的破冰之旅閱讀 228評(píng)論 0 1

友情鏈接更多精彩內(nèi)容