黑马程序员技术交流社区

标题: 【长沙校区】轮播图案例实现 [打印本页]

作者: 长沙-小知姐姐    时间: 2019-2-12 14:38
标题: 【长沙校区】轮播图案例实现
本帖最后由 长沙-小知姐姐 于 2019-2-21 15:01 编辑



作为在市面上各个应用最最常见的功能-轮播图,想必大家并不陌生了,在电商相关的网站以及app中是最常见的,随便打开一个电商类的,在首页都能看到轮播图效果。既然是这么常见的效果,有很多人都想着把这个功能做得比较炫丽,比较好看,别人一眼看去觉得高大上,下面呢,我为大家讲解一下 旋转木马轮播图的效果是如何实现;下面我们先来看一下效果

案例效果
所谓外行看热闹,内行看门道,接触过前端开发的小伙伴 应该有思路来进行实现,暂且不管其他实现思路,先来谈谈我是怎么来实现这个功能的


布局结构                                          
1.首先,进行布局结构,利用div包含ul,每一个img利用li来进行包裹,这也是轮播图实现的标准结构,左右两个箭头利用新的盒子来包裹,然后进行定位

<div class="wrap" id="wrap">
        <div class="slide" id="slide">
            <ul>
                <li>
                    <a href="#"><img src="images/slidepic1.jpg" alt="" /></a>
                </li>
                <li>
                    <a href="#"><img src="images/slidepic2.jpg" alt="" /></a>
                </li>
                <li>
                    <a href="#"><img src="images/slidepic3.jpg" alt="" /></a>
                </li>
                <li>
                    <a href="#"><img src="images/slidepic4.jpg" alt="" /></a>
                </li>
                <li>
                    <a href="#"><img src="images/slidepic5.jpg" alt="" /></a>
                </li>
            </ul>
            <div class="arrow" id="arrow">
                <a href="javascript:;" class="prev" id="arrLeft"></a>
                <a href="javascript:;" class="next" id="arrRight"></a>
            </div>
        </div>
</div>

下面是相关样式

@charset "UTF-8";
/*初始化  reset*/
blockquote, body, button, dd, dl, dt, fieldset, form, h1, h2, h3, h4, h5, h6, hr, input, legend, li, ol, p, pre, td, textarea, th, ul {
    margin: 0;
    padding: 0
}

body, button, input, select, textarea {
    font: 12px/1.5 "Microsoft YaHei", "微软雅黑", SimSun, "宋体", sans-serif;
    color: #666;
}

ol, ul {
    list-style: none;
}

a {
    text-decoration: none;
}

fieldset, img {
    border: 0;
    vertical-align: top;
}
a, input, button, select, textarea {
    outline: none;
}

a, button {
    cursor: pointer;
}

.wrap {
    width: 1200px;
    margin: 100px auto;
}

.slide {
    height: 500px;
    position: relative;
}

.slide li {
    position: absolute;
    left: 200px;
    top: 0;
}

.slide li img {
    width: 100%;
}

.arrow {
    opacity: 0;
    position: absolute;
    top: 50%;
    z-index: 1000;
    width: 100%;
}
.prev, .next {
    width: 76px;
    height: 112px;
    position: absolute;
    z-index: 99;
}
/*箭头的图片*/
.prev {
    left: 0;
    background: url(../images/prev.png) no-repeat;
}

.next {
    right: 0;
    background-image: url(../images/next.png);
}

小伙伴们也看到了,在这个样式里面,我根本没有去设置li的位置,只设置了定位,那么这几张图片肯定是叠加起来的,下面就需要利用javascript代码来进行控制;

定义li的配置单
首先,先给每一个盒子来设置它应该在的位置
从效果来看,这个旋转木马有一个3D的效果,离得近的,图片大一些,并且层级关系也高一些,离得远的,小一些,并且层级关系低一些,根据这些效果,我们可以把5个盒子的位置先固定好
例如:1号盒子跟5号盒子在一个层级,那么它们的缩放的宽,层级,透明度,应该是一样的,只是5号盒子的偏移比1号盒子要多

1号盒子:
width: 400,
left: 50,
top: 20,
opacity: 0.2,
zIndex: 2
5号盒子:
width: 400,
left: 750,
top: 20,
opacity: 0.2,
zIndex: 2

同理,2号盒子和4号盒子在一个层级,相应的宽,层级,透明度应该是一样,只是4号盒子的偏移比1号盒子要多,而且顶部的偏移也需要比上一个层级的要多,这样产生一个3D效果

2号盒子:
width: 600,
top: 70,
left: 0,
opacity: 0.8,
zIndex: 3
4号盒子:
width: 600,
top: 70,
left: 600,
opacity: 0.8,
zIndex: 3

3号盒子在最中心,也就是用户看到的图片,那么它的宽应该就是图片的宽度了,而且层级是最高的,完全不透明

width: 800,
top: 100,
left: 200,
opacity: 1,
zIndex: 4

这样没一个盒子的位置就固定好了,那么我们可以把这些值封装到对象中,方便我们去取

var config = [{
            width: 400,//宽度
            top: 20,//顶部偏移
            left: 50,//左偏移
            opacity: 0.2,//透明度
            zIndex: 2//层级
        }, //0
        {
            width: 600,
            top: 70,
            left: 0,
            opacity: 0.8,
            zIndex: 3
        }, //1
        {
            width: 800,
            top: 100,
            left: 200,
            opacity: 1,
            zIndex: 4
        }, //2
        {
            width: 600,
            top: 70,
            left: 600,
            opacity: 0.8,
            zIndex: 3
        }, //3
        {
            width: 400,
            top: 20,
            left: 750,
            opacity: 0.2,
            zIndex: 2
        } //4
    ];

位置已经定义好了,接下来需要的就是把这些值设置给每一个li,当页面一加载,所有图片是以动画的形式到了执行的位置,那么我们先需要来定义动画函数,这里如果想简单实现,不必定义动画函数,在li上面添加过渡样式即可

动画函数

/**
* 封装动画的函数
* @param {*} obj 传递过来的执行动画的元素
* @param {*} json 需要执行什么要的动画
* @param {*} fn 执行完毕的回调
*/
function animate(obj, json, fn) {
    //保证只会开启一个定时器执行动画
    clearInterval(obj.timer);
    //开启定时器
    obj.timer = setInterval(function () {
        //控制动画是否执行到目标值的标记
        var flag = true;
        //遍历传递过来的对象
        for (var k in json) {
            if (k === "opacity") {//透明度的变化
                //opacity的值是 0 - 1,先乘以100 方便计算
                //leader 就是元素当前的属性值
                var leader = getStyle(obj, k) * 100;
                //元素目标属性值
                var target = json[k] * 100;
                //算出每次需要变化的值
                var step = (target - leader) / 10;
                //取整
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                leader = leader + step;
                //重新把值设置给元素
                obj.style[k] = leader / 100;
            } else if (k === "zIndex") {//堆叠顺序的设置
obj.style.zIndex = json[k];
            } else {
                var leader = parseInt(getStyle(obj, k)) || 0;
                var target = json[k];
                var step = (target - leader) / 10;
                step = step > 0 ? Math.ceil(step) : Math.floor(step);
                leader = leader + step;
                obj.style[k] = leader + "px";
            }
            if (leader != target) {//如果当前属性值不等于目标值,说明还需要执行动画
                flag = false;
            }
        }
        if (flag) {//
            clearInterval(obj.timer);
            if (fn) {
                fn();//动画执行完毕之后的回调
            }
        }
    }, 15);
}
/**
* 获取元素对象的属性
* @param {*} obj 元素对象
* @param {*} attr 获取什么属性的值
*/
function getStyle(obj, attr) {
    if (window.getComputedStyle) {
        return window.getComputedStyle(obj, null)[attr];
    } else {
        return obj.currentStyle[attr];
    }
}

把配置单里面的值设置给每一个li

获取li的集合,然后进行遍历,config里面对应的配置对象取出来,然后设置给li,这里还有一个小细节需要来处理,默认左右箭头是不显示的,只有当鼠标 移入到轮播图的时候才显示
//找人
var wrap = document.getElementById("wrap");
var arrow = document.getElementById("arrow");
var slide = document.getElementById("slide");
var ul = slide.children[0];
var lis = ul.children;//所有图片
//1.鼠标经过轮播图 让箭头渐渐地显示 鼠标离开渐渐消失
wrap.onmouseover = function () {
        animate(arrow, {"opacity": 1});
};
wrap.onmouseout = function () {
        animate(arrow, {"opacity": 0});
};
//获取页面上所有的li 让他们从当前的位置 以动画的效果到指定的位置
for (var i = 0; i < lis.length; i++) {
        animate(lis, config);
}


给左右按钮添加点击事件
每一个盒子的位置都是固定好了的,我们每次都是让一个li来移动到相应的,例如:点击的右箭头,那么5号盒子应该移到1号盒子的位置,1号盒子移动到2号盒子的位置,2号盒子移动到3号盒子的位置,3号盒子移动到4号盒子的位置,4号盒子移动到5号盒子的位置,如图:



而我们的每一个位置的相关属性都存在数组中,那么我们就可以利用数组方法来巧妙的实现这个效果

//点击右箭头
arrRight.onclick = function () {
        //把开始的元素放到最后
        config.push(config.shift());
        assign();//重新设置li的位置
};
//点击左箭头
arrLeft.onclick = function () {
        //把最后的元素放到开始
        config.unshift(config.pop());
        assign();
};



添加节流阀

当我们点击按钮,让li在执行动画的时候,不应该再让用户来触发动画,防止数据错乱,用一个boolean变量来进行控制
window.onload = function() {
    ...

    //获取页面上所有的li 让他们从当前的位置 以动画的效果到指定的位置
    function assign() {
        for (var i = 0; i < lis.length; i++) {
            animate(lis, config, function() {
                flag = true; //动画执行完成后重新打开阀门
            });
        }
    }

    assign();
    //3.点击箭头旋转
    //点击右箭头
    arrRight.onclick = function() {
        if (flag) {
            flag = false; //关闭阀门
            //把开始的元素放到最后
            ...
        }
    };
    //点击左箭头
    arrLeft.onclick = function() {
        if (flag) {
            flag = false;
            //把最后的元素放到开始
            ...
        }

    };
    //4.添加节流阀
    var flag = true; //表示阀门是打开的

};

完整js代码

window.onload = function() {
    //找人
    var wrap = document.getElementById("wrap");
    var arrow = document.getElementById("arrow");
    var arrLeft = document.getElementById("arrLeft");
    var arrRight = document.getElementById("arrRight");
    var slide = document.getElementById("slide");
    var ul = slide.children[0];
    var lis = ul.children; //所有图片
    //1.鼠标经过轮播图 让箭头渐渐地显示 鼠标离开渐渐消失
    wrap.onmouseover = function() {
        animate(arrow, { "opacity": 1 });
    };
    wrap.onmouseout = function() {
        animate(arrow, { "opacity": 0 });
    };
    //2.设置图片位置,把每一个位置对应的属性记录在对象中,然后把对象放在数组中,方便去取

var config = [{
            width: 400,
            top: 20,
            left: 50,
            opacity: 0.2,
            zIndex: 2
        }, //0
        {
            width: 600,
            top: 70,
            left: 0,
            opacity: 0.8,
            zIndex: 3
        }, //1
        {
            width: 800,
            top: 100,
            left: 200,
            opacity: 1,
            zIndex: 4
        }, //2
        {
            width: 600,
            top: 70,
            left: 600,
            opacity: 0.8,
            zIndex: 3
        }, //3
        {
            width: 400,
            top: 20,
            left: 750,
            opacity: 0.2,
            zIndex: 2
        } //4
    ]; //其实就是一个配置单 规定了每张图片的大小位置层级透明度

  //获取页面上所有的li 让他们从当前的位置 以动画的效果到指定的位置
    function assign() {
        for (var i = 0; i < lis.length; i++) {
            animate(lis, config, function() {
                flag = true; //动画执行完成后重新打开阀门
            });
        }
    }

    assign();
    //3.点击箭头旋转
    //点击右箭头
    arrRight.onclick = function() {
        if (flag) {
            flag = false; //关闭阀门
            //把开始的元素放到最后
            config.push(config.shift());
            assign();
        }
    };
    //点击左箭头
    arrLeft.onclick = function() {
        if (flag) {
            flag = false;
            //把最后的元素放到开始
            config.unshift(config.pop());
            assign();
        }

    };
//4.添加节流阀
    var flag = true; //表示阀门是打开的

};









欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2