Javascript使用三大家族和事件来DIY动画效果相关笔记(一)

1.offset家族

◆offsetWidth和offsetHeight表示盒子真实的宽度高度,这个真实的宽度包括 四周的边框、四周的padding、及定义的宽度高度或内容撑开的高度和宽度,可以用来检测盒子实际的大小,属性也是只读不可写的,返回的是不带单位的数值,返回值为number类型。

◆offsetLeft和offsetTop表示当前盒子在定位后距离定位了的父容器的左偏移与上偏移,该属性是只读的不能写,如果父容器没有定位属性(主要是非静态定位),那么就以距离自己最近的父系的非静态定位的盒子为基准,其实和样式中的非静态定位一样的,实在找不到就以浏览器的起始点为基准,所以也许浏览器的起始点是用了非静态定位的,offsetLeft和offsetTop如果在当前盒子没有定位时,那么就默认一浏览器的起始点为基准了,返回的是不带单位的数值,返回值为number类型。

◆offsetParent表示当前盒子距离最近的父系非静态的定位盒子元素,offsetParent返回的是一个元素节点,如果父系盒子中都没有非静态定位,那么就默认返回body节点了。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>offset家族</title>
    <style type="text/css">
        #box {
            border: 5px solid #0f0;
            padding: 10px;
            width: 200px;
            height: 200px;
            background-color: #f00;
            position: relative;
        }

        #box1 {
            position: absolute;
            left: 20px;
            top: 20px;
            width: 60px;
            height: 60px;
            background-color: #0f0;
        }

        #box2 {
            position: absolute;
            left: 100px;
            top: 100px;
            width: 60px;
            height: 60px;
            background-color: #f09;
        }

        #box3 {
            position: absolute;
            left: 300px;
            top: 300px;
            width: 160px;
            height: 160px;
            background-color: #901;
        }

    </style>
</head>
<body>
<div id="box">
    <div id="box1"></div>
    <div id="box2"></div>
</div>

<div id="box3"></div>
<script>
    /*
     * offsetWidth和offsetHeight
     * 表示盒子真实的宽度高度,这个真实的宽度包括
     * 四周的边框、四周的padding、及定义的宽度高度或内容撑开的高度和宽度,
     * 可以用来检测盒子实际的大小,属性也是只读不可写的,
     * 返回的是不带单位的数值,返回值为number类型。
     */
    console.log(box.offsetWidth);//盒子实际宽度
    console.log(box.offsetHeight);//盒子实际高度


    /*
     * offsetLeft和offsetTop
     * 表示当前盒子在定位后距离定位了的父容器的左偏移与上偏移,
     * 该属性是只读的不能写,如果父容器没有定位属性(主要是非静态定位),
     * 那么就以距离自己最近的父系的非静态定位的盒子为基准,
     * 其实和样式中的非静态定位一样的,实在找不到就以浏览器的起始点为基准,
     * 所以也许浏览器的起始点是用了非静态定位的,
     * offsetLeft和offsetTop如果在当前盒子没有定位时,
     * 那么就默认一浏览器的起始点为基准了,
     * 返回的是不带单位的数值,返回值为number类型。
     */


    console.log(box1.offsetLeft);//盒子距离父容器的左偏移
    console.log(box1.offsetTop);//盒子距离父容器的上偏移


    /*
     *offsetParent表示当前盒子距离最近的父系非静态的定位盒子元素,
     * offsetParent返回的是一个元素节点,
     * 如果父系盒子中都没有非静态定位,
     * 那么就默认返回body节点了。
     */
    console.log(box2.offsetParent);//box2找到了非静态定位的父容器 返回父容器节点
    console.log(box3.offsetParent);//box3找不到非静态定位的父容器 返回body节点

    /*
     *offsetLeft和style.left的区别
     *◆offsetLeft只读,style.left可读可写。
     *
     *◆在没有设置行内样式定位等属性时,style.left能获取到的只是空
     *字符串,而offsetLeft能够获取真实的偏移值。
     *
     *◆在设置了行内样式定位等属性时,style.left获取到的只是带有单
     *位的字符串如果200px,而offsetLeft获取还是真实的偏移值如200,
     *offsetLeft获取到的是number类型的数字。
     *
     *◆offsetLeft在当前盒子不处于定位的状态下也能够针对浏览器的
     *起始点来确定偏移值,而style.left在那个时候只能够获取空字符串。
     *
     *★相同点,在定位的时候二者一样的,都不会去计算非静态定位的
     *父容器的边框,无论边框多大,都是从padding开始算起的,那个
     *时候style.left去掉单位转换为数字就等于offsetLeft了。
     */

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


2.非静态定位小知识

◆给元素添加非静态定位的定位属性时,你如果不设置它的left和top或者bottom再或者right属性时,他就装作以标准文档流的方式找个位置待着,但是它的确不占空间,很像是左浮动,也许非静态定位不设置left、top、right、bottom属性时就等于float:left;,定位的时候left、top、right、bottom属性不会计算非静态定位的父容器的边框,而是从父容器去除边框之后才正式开始计算的,但是自己如果是有边框的话,会以自己边框最外层作为left、top、right、bottom的起始点也就是(0,0)点,会以这个点与父容器去除边框后的(0,0)点重合,只再根据left、top、right、bottom来确定位置。


3.offsetLeft和style.left的区别

◆offsetLeft只读,style.left可读可写。

◆在没有设置行内样式定位等属性时,style.left能获取到的只是空字符串,而offsetLeft能够获取真实的偏移值。
◆在设置了行内样式定位等属性时,style.left获取到的只是带有单位的字符串如果200px,而offsetLeft获取还是真实的偏移值如200,offsetLeft获取到的是number类型的数字。
◆offsetLeft在当前盒子不处于定位的状态下也能够针对浏览器的起始点来确定偏移值,而style.left在那个时候只能够获取空字符串。

★相同点,在定位的时候二者一样的,都不会去计算非静态定位的父容器的边框,无论边框多大,都是从padding开始算起的,那个时候style.left去掉单位转换为数字就等于offsetLeft了。


4.使用offsetLeft和style.left来DIY动画

◆基础的闪动与匀速移动效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>通过offset来实现动画</title>
    <style type="text/css">

        #div1 {
            width: 80px;
            height: 80px;
            background-color: #f0f;
            position: absolute;
            top: 200px;
        }

        #div2 {
            width: 80px;
            height: 80px;
            background-color: #f09;
            position: absolute;
            top: 400px;
        }

    </style>
</head>
<body>
<button id="btn1">瞬间闪动</button>
<button id="btn2">匀速运动</button>
<div id="div1"></div>
<div id="div2"></div>
<script>
    btn1.addEventListener("click", function () {
        div1.style.left = "800px";
    })
    btn2.addEventListener("click", function () {
        //定时器,每隔一定的时间向右走一些
        setInterval(function () {
            //这么做太麻烦了
//            div2.style.left=parseInt(div2.style.left==""?0:div2.style.left)+10+"px";

            //动画原理: 盒子未来的位置 = 盒子现在的位置 + 步长;
            //style.left赋值,用offsetLeft获取值。
            //style.left获取值不方便很麻烦,首先要设置行内式,如果没有设置行内式获取的是"";
            //如果不通过判断来重新赋值就容易出现parseInt(style.left)时返回值是NaN
            //offsetLeft是只读的,每次获取到的值都是number类型的数字。
            div2.style.left = div2.offsetLeft + 10 + "px";
        }, 30);
    })
</script>
</body>
</html>
◆封装基础的匀速动画简单版
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用offset封装动画</title>
    <style type="text/css">
        #box {
            height: 200px;
            background-color: #eee;
            position: relative;
        }

        #box1 {
            width: 100px;
            height: 100px;
            position: absolute;
            top: 50px;
            background-color: #f09;
            left: 1312px;
        }
    </style>
</head>
<body>
<div>
    <div id="box">
        <button id="btn1">移动到200</button>
        <button id="btn2">移动到400</button>
        <div id="box1"></div>
    </div>
    <script>
        var box1 = document.getElementById("box1");
        var timer = 0;
        var speed = 10;
        btn1.onclick = function () {
            animate(200)
        }
        btn2.onclick = function () {
            animate(400)
        }

        function animate(target) {
            //bug1:当你多点几次的时候会出现 多个定时器同时运行,然后会导致停不下也无法关闭
            //解决方案:每次执行动画时就停止上一次的动画 清除上一次的动画后再继续本次的动画
            clearInterval(timer);

            //bug2:当移动到指定的位置后再点击动画 会让动画继续跑然后还会停不下来 因为已经超过了要移动的位置
            //解决方案:先判断是否符合距离 然后再执行是否继续移动
//            if(box1.offsetLeft===target)

            //bug3:如果位移多次自增后无法和指定位置相同,就会导致无法关闭循环定时器而不停下来
            //解决方案:判断 指定位置target减去偏移offsetLeft是否小于自增距离speed,
            //          如果小就停止,并且让style.left等于target
//            if (target - box1.offsetLeft < speed) {
//                box1.style.left=target+"px";

            //bug4:如果当偏移本身就超过了指定的位置,就会出现突然间就跑到指定位置了
            //解决方案:如果一开始就超过了指定的位置,那么就让它往回跑,达到指定位置
            // 最重要的是判断正负的距离是否在自增距离的范围内 speed的范围内
            // 如果在,那么久直接赋值为指定的位置,然后停止循环计时器,终止方法执行

            //动态改变每次递增的距离  如果跑过了递增的距离就要为负数
            speed=box1.offsetLeft>target?-10:10;
            timer = setInterval(function () {

                //让正负距离不能够超过步长即自增的距离,不管正负
                if (Math.abs(target - box1.offsetLeft) <= Math.abs(speed)) {

                    box1.style.left=target+"px";
                    clearInterval(timer);
                    return;//终止函数往下进行
                }
                box1.style.left = box1.offsetLeft + speed + "px";
            }, 30)
        }
    </script>
</div>
</body>
</html>
◆封装基础的匀速动画正常版
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用offset封装动画</title>
    <style type="text/css">
        #box {
            height: 300px;
            background-color: #eee;
            position: relative;
        }
/*box1和box2这种颜色搭配很晃眼,很有创意*/
        #box1 {
            width: 100px;
            height: 100px;
            position: absolute;
            top: 50px;
            background-color: #f09;
            left: 1312px;
        }
        #box2 {
            width: 100px;
            height: 100px;
            position: absolute;
            top: 150px;
            background-color: #90f;
            left: 0px;
        }
    </style>
</head>
<body>
<div>
    <div id="box">
        <button id="btn1">移动到200</button>
        <button id="btn2">移动到400</button>
        <div id="box1"></div>
        <div id="box2"></div>
    </div>
    <script>
        var box1 = document.getElementById("box1");
        var box2 = document.getElementById("box2");
        //不需要自定义 定时器 和 步数了 直接给元素添加这两个属性即可
        //        var timer = 0;
        //        var speed = 10;
        btn1.onclick = function () {
            animate(box1, 200);//直接传递元素即可
            animate(box2, 200);//直接传递元素即可
        }
        btn2.onclick = function () {
            animate(box1, 400)//直接传递元素即可
            animate(box2, 400)//直接传递元素即可
        }

        function animate(element, target) {
            //bug1:当你多点几次的时候会出现 多个定时器同时运行,然后会导致停不下也无法关闭
            //解决方案:每次执行动画时就停止上一次的动画 清除上一次的动画后再继续本次的动画
            clearInterval(element.timer);//这样每个元素都有一个独立的定时器的计数器了

            //bug2:当移动到指定的位置后再点击动画 会让动画继续跑然后还会停不下来 因为已经超过了要移动的位置
            //解决方案:先判断是否符合距离 然后再执行是否继续移动
//            if(box1.offsetLeft===target)

            //bug3:如果位移多次自增后无法和指定位置相同,就会导致无法关闭循环定时器而不停下来
            //解决方案:判断 指定位置target减去偏移offsetLeft是否小于自增距离speed,
            //          如果小就停止,并且让style.left等于target
//            if (target - box1.offsetLeft < speed) {
//                box1.style.left=target+"px";

            //bug4:如果当偏移本身就超过了指定的位置,就会出现突然间就跑到指定位置了
            //解决方案:如果一开始就超过了指定的位置,那么就让它往回跑,达到指定位置
            // 最重要的是判断正负的距离是否在自增距离的范围内 speed的范围内
            // 如果在,那么久直接赋值为指定的位置,然后停止循环计时器,终止方法执行

            //动态改变每次递增的距离  如果跑过了递增的距离就要为负数
            element.speed = element.offsetLeft > target ? -10 : 10;
            element.timer = setInterval(function () {

                //让正负距离不能够超过步长即自增的距离,不管正负
                if (Math.abs(target - element.offsetLeft) <= Math.abs(element.speed)) {

                    element.style.left = target + "px";
                    clearInterval(element.timer);
                    return;//终止函数往下进行
                }
                element.style.left = element.offsetLeft + element.speed + "px";
            }, 30)
        }
    </script>
</div>
</body>
</html>


5.图片轮播基础之匀速轮播

◆使用封装的匀速动画来DIY滑动轮播图,也就是鼠标移动到123456这些数字上后,图片以匀速滑动的方式进行切换。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用封装的匀速动画制作滑动轮播图</title>
    <style type="text/css">

        body, ul, li, div, span, img {
            padding: 0;
            margin: 0;
        }

        img {
            border: 0 none;
            vertical-align: top;
        }

        .box {
            width: 490px;
            height: 170px;
            padding: 5px;
            border: 1px solid #f09;
            margin: 100px auto;
        }

        .inner {
            width: 490px;
            height: 170px;
            position: relative;
            overflow: hidden;
            cursor: pointer;
        }

        ul {
            list-style: none;
            width: 500%;
            position: absolute;
            left: 0;
        }

        li {
            float: left;
        }

        .square {
            position: absolute;
            bottom: 10px;
            right: 10px;
        }

        .square span {
            display: inline-block;
            width: 16px;
            height: 16px;
            line-height: 14px;
            border: 1px solid #ccc;
            background-color: #fff;
            text-align: center;
            margin-left: 5px;
            cursor: pointer;
        }

        .square .current {
            background-color: #f00;
            color: #fff;
        }
    </style>
</head>
<body>
<div class="box">
    <div class="inner" id="inner">
        <ul>
            <li><img src="images1/01.jpg"></li>
            <li><img src="images1/02.jpg"></li>
            <li><img src="images1/03.jpg"></li>
            <li><img src="images1/04.jpg"></li>
            <li><img src="images1/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>

    //需求:当点击 轮播图中的 span时 span要高亮显示并且
    // 还要让图片框中的图片移动到相应的图片上
    //思路:
    //  1.使用循环来遍历span,给每一个span绑定鼠标移入的事件
    //  2.当鼠标移入span时就清除全部span的className属性
    //  3.然后给自己加上 高亮的class(排他思想 先杀死全部 然后复活自己)
    //  4.给每个元素添加一个index属性,作为图片的相应索引
    //  5.调用自己做的匀速动画框架,传递元素和相应的指定的位置(-(图片索引*图片宽度))
    //步骤:
    //  1.获取事件源及相关元素对象
    //  2.绑定事件
    //  3.书写事件驱动程序

    //  1.获取事件源及相关元素对象
    var inner = document.getElementById("inner");
    var ul = inner.firstElementChild || inner.firstChild;
    var spanArr = inner.children[1].children;
    
    //  2.绑定事件
    for (var i = 0; i < spanArr.length; i++) {
        //给每个元素添加一个index属性
        spanArr[i].index=i;
        spanArr[i].onmouseover= function () {

            //清除全部span的className属性
            for(var j=0;j < spanArr.length; j++){
                spanArr[j].className="";
            }

            //给自己加上 高亮的class(排他思想 先杀死全部 然后复活自己)
            this.className="current";

            //调用自己做的匀速动画框架,传递元素和相应的指定的位置(-(图片索引*图片宽度))
//            animate(ul,-this.index*490);
            animate(ul,-this.index*inner.offsetWidth);
            //动画的本质就是左右移动元素,移动至指定的位置,
            // 由于元素的父容器设置了高宽也设置了overflow:hiden
            // 所以你只能看到指定部分,于是就是轮播了。
        }

    }
    //  3.书写事件驱动程序

    //自己制作的匀速动画的框架
    function animate(element, target) {
        //bug1:当你多点几次的时候会出现 多个定时器同时运行,然后会导致停不下也无法关闭
        //解决方案:每次执行动画时就停止上一次的动画 清除上一次的动画后再继续本次的动画
        clearInterval(element.timer);//这样每个元素都有一个独立的定时器的计数器了

        //bug2:当移动到指定的位置后再点击动画 会让动画继续跑然后还会停不下来 因为已经超过了要移动的位置
        //解决方案:先判断是否符合距离 然后再执行是否继续移动
//            if(box1.offsetLeft===target)

        //bug3:如果位移多次自增后无法和指定位置相同,就会导致无法关闭循环定时器而不停下来
        //解决方案:判断 指定位置target减去偏移offsetLeft是否小于自增距离speed,
        //          如果小就停止,并且让style.left等于target
//            if (target - box1.offsetLeft < speed) {
//                box1.style.left=target+"px";

        //bug4:如果当偏移本身就超过了指定的位置,就会出现突然间就跑到指定位置了
        //解决方案:如果一开始就超过了指定的位置,那么就让它往回跑,达到指定位置
        // 最重要的是判断正负的距离是否在自增距离的范围内 speed的范围内
        // 如果在,那么久直接赋值为指定的位置,然后停止循环计时器,终止方法执行

        //动态改变每次递增的距离  如果跑过了递增的距离就要为负数
        element.speed = element.offsetLeft > target ? -10 : 10;
        element.timer = setInterval(function () {

            //让正负距离不能够超过步长即自增的距离,不管正负
            if (Math.abs(target - element.offsetLeft) <= Math.abs(element.speed)) {

                element.style.left = target + "px";
                clearInterval(element.timer);
                return;//终止函数往下进行
            }
            element.style.left = element.offsetLeft + element.speed + "px";
        }, 10)
    }

</script>
</body>
</html>
◆使用封装的匀速动画来DIY左右轮播图,也就是点击左右按钮的时候,图片左右匀速切换。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用封装的匀速动画制作左右轮播图</title>
    <style type="text/css">

        body, ul, li, div, span, img {
            padding: 0;
            margin: 0;
        }

        img {
            border: 0 none;
            vertical-align: top;
        }

        .box {
            width: 520px;
            height: 280px;
            padding: 5px;
            border: 1px solid #f09;
            margin: 100px auto;
        }

        .inner {
            width: 520px;
            height: 280px;
            position: relative;
            overflow: hidden;
            cursor: pointer;
        }

        ul {
            list-style: none;
            width: 500%;
            position: absolute;
            left: 0;
        }

        li {
            float: left;
        }
        /*鼠标移动到图片框时 就显示*/
        .inner:hover .square {
            display: block;
        }

        .square {
            width: 100%;
            position: absolute;
            top:50%;
            margin-top:-20px;
            display: none;
            /*默认不显示*/
        }

        .square span {
            display: block;
            width: 30px;
            height: 60px;
            background-color: #fff;
            font:500 25px/60px "consolas";
            text-align: center;
            cursor: pointer;
            color:#fff;
            background-color: rgba(0,0,0,0.3);
        }
        #sq-left {
            float:left;
        }
        #sq-right {
            float: right;
        }

        /*.square .current {*/
            /*background-color: #f00;*/
            /*color: #fff;*/
        /*}*/
    </style>
</head>
<body>
<div class="box">
    <div class="inner" id="inner">
        <ul>
            <li><img src="images2/1.jpg"></li>
            <li><img src="images2/2.jpg"></li>
            <li><img src="images2/3.jpg"></li>
            <li><img src="images2/4.jpg"></li>
            <li><img src="images2/5.jpg"></li>
        </ul>
        <div class="square">
            <span id="sq-left"><</span>
            <span id="sq-right">></span>
        </div>
    </div>
</div>
<script>
    //需求:当点击左箭头时,图片向左切换一张,
    //      切换到第一张时就弹出 已经是第一张,
    //      便不能再切换往前切换了,点击右箭头时
    //      图片向右切换一张,切换到最后一张时,
    //      就弹出 已经是最后一张了,便不能再往后切换了
    //思路:给左右箭头绑定单击事件,点击左箭头或者右箭头,就让移动的距离增大,
    //      增大的倍数就是 图片的索引,每次点击左右箭头之前,先进行判断,
    //      左箭头判断是否小于0,右箭头判断是否大于最大索引
    //步骤:
    // 1.   获取事件源及相关元素对象
    // 2.   绑定事件
    // 3.   书写事件驱动程序

    // 1.   获取事件源及相关元素对象
    var inner=document.getElementById("inner");
    var ul=inner.children[0];
    var sqleft=document.getElementById('sq-left');
    var sqright=document.getElementById('sq-right');

    // 2.   绑定事件
    var index=0;//图片索引
    sqleft.onclick= function () {
        // 3.   书写事件驱动程序

        index--;//索引减减
        if(index<0){
            index=0;
            alert("已经是第一张了");
            return;
        }
        animate(ul, -inner.offsetWidth*index);
    }
    sqright.onclick= function () {
        // 3.   书写事件驱动程序

        index++;//索引加加
        if(index>ul.children.length-1){
            index=ul.children.length-1;
            alert("已经是最后一张了");
            return;
        }
        animate(ul, -inner.offsetWidth*index);
    }
    // 3.   书写事件驱动程序

    //自己制作的匀速动画的框架
    function animate(element, target) {
        //bug1:当你多点几次的时候会出现 多个定时器同时运行,然后会导致停不下也无法关闭
        //解决方案:每次执行动画时就停止上一次的动画 清除上一次的动画后再继续本次的动画
        clearInterval(element.timer);//这样每个元素都有一个独立的定时器的计数器了

        //bug2:当移动到指定的位置后再点击动画 会让动画继续跑然后还会停不下来 因为已经超过了要移动的位置
        //解决方案:先判断是否符合距离 然后再执行是否继续移动
//            if(box1.offsetLeft===target)

        //bug3:如果位移多次自增后无法和指定位置相同,就会导致无法关闭循环定时器而不停下来
        //解决方案:判断 指定位置target减去偏移offsetLeft是否小于自增距离speed,
        //          如果小就停止,并且让style.left等于target
//            if (target - box1.offsetLeft < speed) {
//                box1.style.left=target+"px";

        //bug4:如果当偏移本身就超过了指定的位置,就会出现突然间就跑到指定位置了
        //解决方案:如果一开始就超过了指定的位置,那么就让它往回跑,达到指定位置
        // 最重要的是判断正负的距离是否在自增距离的范围内 speed的范围内
        // 如果在,那么久直接赋值为指定的位置,然后停止循环计时器,终止方法执行

        //动态改变每次递增的距离  如果跑过了递增的距离就要为负数
        element.speed = element.offsetLeft > target ? -10 : 10;
        element.timer = setInterval(function () {

            //让正负距离不能够超过步长即自增的距离,不管正负
            if (Math.abs(target - element.offsetLeft) <= Math.abs(element.speed)) {

                element.style.left = target + "px";
                clearInterval(element.timer);
                return;//终止函数往下进行
            }
            element.style.left = element.offsetLeft + element.speed + "px";
        }, 10)
    }

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

◆使用封装的匀速动画来DIY无缝轮播图,轮播的本质就是来回移动图片的位置,无缝轮播其实是多加了一张图片在最后面,当你切换到最后一张图片时,最后一张图片再往后切换时,实际上会瞬间切换到第一张然后再继续切换匀速切换到第二张,因为最后一张和第一张一模一样,所以瞬间切换的过程根本看不出来,所以就像很完整的无缝轮播了。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用封装的匀速动画制作无缝轮播图</title>
    <style type="text/css">

        body, ul, ol, li, div, span, img {
            padding: 0;
            margin: 0;
        }

        img {
            border: 0 none;
            vertical-align: top;
        }

        .box {
            width: 500px;
            height: 200px;
            padding: 5px;
            border: 1px solid #f09;
            margin: 100px auto;
        }

        .inner {
            width: 500px;
            height: 200px;
            position: relative;
            overflow: hidden;
            /*cursor: pointer;*/
        }

        ul {
            list-style: none;
            width: 620%;
            position: absolute;
            left: 0;
        }

        ul li {
            float: left;
        }

        /*鼠标移动到图片框时 就显示*/
        .inner:hover .square {
            display: block;
        }

        .square {
            width: 100%;
            position: absolute;
            top: 50%;
            margin-top: -20px;
            display: none;
            /*默认不显示*/
        }

        .square span {
            display: block;
            width: 30px;
            height: 60px;
            background-color: #fff;
            font: 500 25px/60px "consolas";
            text-align: center;
            cursor: pointer;
            color: #fff;
            background-color: rgba(0, 0, 0, 0.3);
        }

        #sq-left {
            float: left;
        }

        #sq-right {
            float: right;
        }

        ol {
            position: absolute;
            bottom: 10px;
            right: 10px;
        }

        ol li {
            display: inline-block;
            width: 16px;
            height: 16px;
            line-height: 14px;
            font-size: 12px;
            border: 1px solid #ccc;
            background-color: #fff;
            text-align: center;
            margin-left: 5px;
            cursor: pointer;
        }

        ol .current {
            background-color: #f00;
            color: #fff;
        }
    </style>
</head>
<body>
<div class="box">
    <div class="inner" id="inner">
        <ul>
            <li><img src="images3/1.jpg"></li>
            <li><img src="images3/2.jpg"></li>
            <li><img src="images3/3.jpg"></li>
            <li><img src="images3/4.jpg"></li>
            <li><img src="images3/5.jpg"></li>
            <li><img src="images3/1.jpg"></li>
        </ul>
        <ol>
            <li class="current">1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
        </ol>
        <div class="square">
            <span id="sq-left"><</span>
            <span id="sq-right">></span>
        </div>
    </div>
</div>
<script>
    //需求:
    // 1.鼠标移动到带有小编号的方块儿时,小方块儿高亮显示并且同时切换到对应的图片
    // 2.让图片从左至右进行自动轮播,小方块儿的高亮样式也会跟着切换
    // 3.鼠标移动到图片框中时,就停止自动轮播
    // 4.点击左右箭头 切换到对应的图片
    // 5.
    // 6.

    //思路:
    //  1.给所有小方块儿绑定鼠标移入的事件,
    //   移入时清除所有的高亮样式然后给当前小方块儿添加高亮样式,
    //   使用自己封装的匀速动画,让图片移动到指定位置
    //  2.封装一个方法,让让图片自动移动到指定位置,小方块儿的高亮样式也会跟着图片切换
    //  3.给图片框设置鼠标移入移出事件,移入的时候就清除执行自动轮播的方法中的定时器
    //    移出时执行自动轮播的定时器
    //  4.给左右箭头绑定单击事件,点击左右箭头时,让方块儿高样式与图片的位置同时切换

    //步骤:
    //1.获取事件源 及相关对象
    //2.绑定事件
    //3.书写事件驱动程序

    //1.获取事件源 及相关对象
    var inner = document.getElementById("inner");
    var ul = inner.children[0];
    var olArr = inner.children[1].children;
    //        var square=inner.lastElementChild ||inner.lastChild;
    //        var sqleft=square.children[0];
    //        var sqright=square.children[1];
    var sqleft = document.getElementById("sq-left");

    var sqright = document.getElementById("sq-right");

    //2.绑定事件 循环给小方块儿绑定移入事件
    for (var i = 0; i < olArr.length; i++) {
        //给每一个ol中的li添加一个index属性,代表当前li的索引值
        olArr[i].index = i;

        olArr[i].onmousemove = function () {
            //3.书写事件驱动程序

            for (var j = 0; j < olArr.length; j++) {
                olArr[j].className = "";
            }
            this.className = "current";
            //使用动画移动距离
            animate(ul, -this.index * inner.offsetWidth);
            //与自动轮播 的 图片索引及方块儿同步
            key = this.index;
            sqindex = this.index;

        }
    }
    //2.绑定事件 给图片框绑定移入移出事件
    inner.onmousemove = function () {
        //清除定时器
        clearInterval(timer);
    }
    inner.onmouseout = function () {
        //调用循环计数器
        timer = setInterval(autoplay, 1000);
    }
    //2.绑定事件 给左右箭头绑定移入移出事件
    sqleft.onclick = function () {
        rautoplay();
    }
    sqright.onclick = function () {
        //自动轮播就是从左往右的
        autoplay();
    }

    /**
     * 自动轮播的方法
     */
    var timer = 0;//全局的定时器 计数器
    var key = 0;//图片的索引
    var sqindex = 0;//方块儿的索引
    function autoplay() {
        key++;//图片索引递增
        //图片比方块儿多一个  当图片在第6张时 方块儿跳到了第一个
        if (key > olArr.length) {
            ul.style.left = 0+"px";//瞬间切换到第一张  看不出来 因为第六张和第一张一模一样
            key = 1;//跳到第二张,因为这个时候方块儿也会跳到第二个
        }


        //使用动画移动距离
        animate(ul, -key * inner.offsetWidth);

        sqindex++;//方块儿的索引递增
        if (sqindex > olArr.length - 1) {
            sqindex = 0;
        }
        for (var i = 0; i < olArr.length; i++) {
            olArr[i].className = "";
        }
        olArr[sqindex].className = "current";
    }

    //调用循环计数器
    timer = setInterval(autoplay, 1000);

    //3.书写事件驱动程序
    /**
     *反方向的自动轮播方法
     */
    function rautoplay() {
        key--;//图片索引递减
        //图片比方块儿多一个  当图片在第1张时 方块儿跳到了最后一个
        if (key < 0) {
            ul.style.left = -olArr.length * inner.offsetWidth+"px";//瞬间切换到最后一张  看不出来 因为第六张和第一张一模一样
            key = olArr.length-1;//跳到倒数第二张,因为这个时候方块儿也会跳到最后一个
        }


        //使用动画移动距离
        animate(ul, -key * inner.offsetWidth);

        sqindex--;//方块儿的索引递减
        if (sqindex < 0) {
            sqindex = olArr.length - 1;
        }
        for (var i = 0; i < olArr.length; i++) {
            olArr[i].className = "";
        }
        olArr[sqindex].className = "current";
    }


    //自己制作的匀速动画的框架
    function animate(element, target) {
        //bug1:当你多点几次的时候会出现 多个定时器同时运行,然后会导致停不下也无法关闭
        //解决方案:每次执行动画时就停止上一次的动画 清除上一次的动画后再继续本次的动画
        clearInterval(element.timer);//这样每个元素都有一个独立的定时器的计数器了

        //bug2:当移动到指定的位置后再点击动画 会让动画继续跑然后还会停不下来 因为已经超过了要移动的位置
        //解决方案:先判断是否符合距离 然后再执行是否继续移动
//            if(box1.offsetLeft===target)

        //bug3:如果位移多次自增后无法和指定位置相同,就会导致无法关闭循环定时器而不停下来
        //解决方案:判断 指定位置target减去偏移offsetLeft是否小于自增距离speed,
        //          如果小就停止,并且让style.left等于target
//            if (target - box1.offsetLeft < speed) {
//                box1.style.left=target+"px";

        //bug4:如果当偏移本身就超过了指定的位置,就会出现突然间就跑到指定位置了
        //解决方案:如果一开始就超过了指定的位置,那么就让它往回跑,达到指定位置
        // 最重要的是判断正负的距离是否在自增距离的范围内 speed的范围内
        // 如果在,那么久直接赋值为指定的位置,然后停止循环计时器,终止方法执行

        //动态改变每次递增的距离  如果跑过了递增的距离就要为负数
        element.speed = element.offsetLeft > target ? -10 : 10;
        element.timer = setInterval(function () {

            //让正负距离不能够超过步长即自增的距离,不管正负
            if (Math.abs(target - element.offsetLeft) <= Math.abs(element.speed)) {

                element.style.left = target + "px";
                clearInterval(element.timer);
                return;//终止函数往下进行
            }
            element.style.left = element.offsetLeft + element.speed + "px";
        }, 10)
    }

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

◆在使用定时器的时候,最好让每一个定时器的编号都独立起来,这样就不容易出现关闭定时器时把别的定时器给关掉了,如给被一个元素弄一个timer属性,当给这个元素设置定时器时就能够让这个元素独享一个定时器了,不会把其它定时器给关掉,定时器的编号timer不要是全局的,否则就会出现关闭定时器时把最后一个定时器关闭,而其他定时器无法关闭。

◆谷歌浏览器的小bug,就是当你给页面元素定位为0时,你会发现,如果让style.left增加,第二次的时候,你会发现offsetLeft会比style.left多1,这个bug会导致动画比你预期的要难,就是根本停不下来,原因是你在轮播之后让页面进行缩放了,才导致这个现象发生。


posted @ 2018-06-25 23:20  我叫贾文利  阅读(159)  评论(0编辑  收藏  举报