很喜欢bilibili上的轮播图造型,这次就来自己写一下B站上的轮播图吧。
首先,B站分旧版和新版,仔细观察会发现,轮播图用的明显不是一套代码,因为旧版轮播图,播到最后一张时就倒回去了,是的,因为每一张图都有过渡,必须遵循这个规则,而且不可能第一张跑在最后一张的后面,这样用户体验不是很好,但我们还是要来做一下这种轮播图。
本次采用Vue框架进行开发,当然原生js和JQuery也可以,但是Vue支持数据双向绑定,响应速度快,简单,更便于维护。

旧版轮播图

代码如下:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }
            a{
                text-decoration: none;
            }
            li{
                list-style-type: none;
            }
            .carousel {
                max-width: 440px;
                height: 220px;
                position: relative;
                overflow: hidden;
                border-radius: 4px;
                float: left;
            }
            .carousel .pic {
                height: 220px;
                transition: 1s ease;
            }
            .carousel img {
                width: 440px;
                height: 220px;
            }
            .carousel .pic li {
                float: left;
            }
            /* 标题过渡 */
            .carousel .title li {
                position: absolute;
                bottom: 10px;
                left: 10px;
                transition: .5s;			
            }
            .carousel .title a {
                color: white;
            }
            .carousel .trig {
                position: absolute;
                right: 35px;
                bottom: 10px;
            }
            .carousel .trig li {
                float: left;
                background-image: url(img/icons.png);
                background-position: -855px -790px;
                width: 18px;
                height: 18px;
                margin-left: 5px;
                cursor: pointer;
            }
            .carousel .trig li:hover {
                background-position: -919px -790px;
            }
            .on {
                background-position: -855px -727px !important;
                width: 18px;
                height: 18px;
            }
            .show {
                opacity: 1;
            }
            .hide {
                opacity: 0;
            }
        </style>
    </head>
    <body>
        <div class="carousel" @mouseover="mouseover" @mouseout="mouseout">
            <ul class="pic" :style="style">
                <li v-for="(item,index) in pics"><a :href="pics[index].url"><img :src="pics[index].src"></a></li>
            </ul>

            <ul class="title">
                <li v-for="(item,index) in pics" :class="pics[index].on?'show':'hide'"><a :href="pics[index].url">{{pics[index].title}}</a></li>

            </ul>

            <ul class="trig">
                <li v-for="(item,index) in pics" :class="{on:pics[index].on}" @click="toggle(index)"></li>
            </ul>
        </div>
        <script>
            var vm = new Vue({
                el: '.carousel',
                data: {
                    index: 0,
                    time: '',
                    pics: [{
                            url: '#',
                            src: 'img/1.jpg',
                            title: '标题1',
                            on: true
                        },
                        {
                            url: '#',
                            src: 'img/2.jpg',
                            title: '标题2',
                            on: false
                        },
                        {
                            url: '#',
                            src: 'img/3.jpg',
                            title: '标题3',
                            on: false
                        },
                        {
                            url: '#',
                            src: 'img/4.jpg',
                            title: '标题4',
                            on: false
                        },
                        {
                            url: '#',
                            src: 'img/5.jpg',
                            title: '标题5',
                            on: false
                        }
                    ]
                },
                methods: {
                    toggle: function(index) {
                        this.index = index;
                        this.pics[index].on = true;
                        for (var i = 0; i < this.pics.length; i++) {
                            if (i !== index)
                                this.pics[i].on = false;
                        }
                    },
                    mouseover: function() {
                        clearInterval(this.time);
                    },
                    mouseout: function() {
                        var that = this;
                        this.time = setInterval(function() {
                            if (that.index == that.pics.length - 1) {
                                that.index = 0;
                                that.pics[0].on = true;
                                that.pics[that.pics.length - 1].on = false;
                            } else {
                                ++that.index;
                                that.pics[that.index].on = true;
                                that.pics[that.index - 1].on = false;
                            }
                        }, 3000)
                    }
                },
                computed: {
                    style: function() {
                        return {
                            marginLeft: this.index * (-100) + '%',
                            width: this.pics.length * 100 + '%'
                        }
                    },
                },
                mounted: function() {
                    this.mouseout();
                }
            });
        </script>
    </body>
</html>

实现效果如图:
avatar
这种轮播图原理是一串图片通过浮动拼在一起,利用marginLeft的位移作为动画实现的,比较容易实现,缺点是中间隔几个图的时候点击感觉比较晃,而且不是无缝轮播。

新版轮播图

接下来我们就来实现一下无缝轮播图,也就是新版B站首页所用的。
代码如下:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }

            a {
                text-decoration: none;
            }

            li {
                list-style-type: none;
            }

            .carousel {
                max-width: 440px;
                height: 220px;
                position: relative;
                overflow: hidden;
                border-radius: 4px;
                float: left;
            }

            .carousel .pic {
                width: 440px;
                height: 220px;

            }

            .carousel img {
                width: 440px;
                height: 220px;
            }

            .carousel .pic li {
                position: absolute;
                top: 0;
                left: 0;
                /* 这里一定要改成绝对定位 */
            }

            /* 标题过渡 */
            .carousel .title li {
                position: absolute;
                bottom: 10px;
                left: 10px;
                transition: .5s;
                z-index: 999999;
            }

            .carousel .title a {
                color: white;
            }

            .carousel .trig {
                position: absolute;
                right: 35px;
                bottom: 10px;
                z-index: 999999;
            }

            .carousel .trig li {
                float: left;
                background-image: url(img/icons.png);
                background-position: -855px -790px;
                width: 18px;
                height: 18px;
                margin-left: 5px;
                cursor: pointer;
            }

            .carousel .trig li:hover {
                background-position: -919px -790px;
            }

            .on {
                background-position: -855px -727px !important;
                width: 18px;
                height: 18px;
            }

            .show {
                opacity: 1;
            }

            .hide {
                opacity: 0;
            }
        </style>
    </head>
    <body>
        <div class="carousel" @mouseover="mouseover" @mouseout="mouseout">
            <ul class="pic" ref="pic">
                <li v-for="(item,index) in pics"><a :href="pics[index].url"><img :src="pics[index].src"></a></li>
            </ul>
            <ul class="title">
                <li v-for="(item,index) in pics" :class="pics[index].on?'show':'hide'"><a :href="pics[index].url">{{pics[index].title}}</a></li>
            </ul>

            <ul class="trig">
                <li v-for="(item,index) in pics" :class="{on:pics[index].on}" @click="toggle(index)"></li>
            </ul>
        </div>
        <script>
            var vm = new Vue({
                el: '.carousel',
                data: {
                    index: 0,
                    time: '',
                    zindex: 1,
                    pics: [{
                            url: '#',
                            src: 'img/1.jpg',
                            title: '标题1',
                            on: true
                        },
                        {
                            url: '#',
                            src: 'img/2.jpg',
                            title: '标题2',
                            on: false
                        },
                        {
                            url: '#',
                            src: 'img/3.jpg',
                            title: '标题3',
                            on: false
                        },
                        {
                            url: '#',
                            src: 'img/4.jpg',
                            title: '标题4',
                            on: false
                        },
                        {
                            url: '#',
                            src: 'img/5.jpg',
                            title: '标题5',
                            on: false
                        }
                    ]
                },
                methods: {
                    toggle: function(i) {
                        this.index = i;
                        this.pics[i].on = true;
                        for (let j = 0; j < this.pics.length; j++) {
                            if (j !== i)
                                this.pics[j].on = false;
                        }
                        this.zindex++;
                        this.$refs.pic.children[i].style.zIndex = this.zindex;
                        this.$refs.pic.children[i].style.transform = 'translate3d(0px,0px,0px)';
                        this.$refs.pic.children[i].style.transition = 'all 0.25s ease 0s';
                        if (i >= 1) {
                            //当前轮播图前面的全部向左移
                            for (let j = 0; j < i; j++) {
                                this.$refs.pic.children[j].style.transition = 'all 0.55s ease 0s';
                                this.$refs.pic.children[j].style.transform = 'translate3d(-440px,0px,0px)';
                            }
                        } else {
                            //点击第一个按钮,此时第一张图在最后一张图左边
                            this.$refs.pic.children[this.pics.length - 1].style.transition = 'all 0.55s ease 0s';
                            this.$refs.pic.children[this.pics.length - 1].style.transform = 'translate3d(440px,0px,0px)';
                        }
                        if (i < this.pics.length - 1) {
                            //当前轮播图后面的全部向右移
                            for (let j = i + 1; j < this.pics.length; j++) {
                                this.$refs.pic.children[j].style.transition = 'all 0.55s ease 0s';
                                this.$refs.pic.children[j].style.transform = 'translate3d(440px,0px,0px)';
                            }
                        } else {
                            //点击最后一个按钮,此时第一张图在最后一张图左边
                            this.$refs.pic.children[0].style.transition = 'all 0.55s ease 0s';
                            this.$refs.pic.children[0].style.transform = 'translate3d(-440px,0px,0px)';
                        }
                    },
                    mouseover: function() {
                        clearInterval(this.time);
                        //鼠标移入,第一张图在最后一张图左边
                        if (this.index == this.pics.length - 1) {
                            this.$refs.pic.children[0].style.transform = 'translate3d(-440px,0px,0px)';
                        }
                        //鼠标移入,第一张图在最后一张图左边
                        else if(this.index ==0){
                            this.$refs.pic.children[this.pics.length - 1].style.transform = 'translate3d(440px,0px,0px)';
                        }
                    },
                    mouseout: function() {
                        var that = this;
                        var i = that.index;
                        //鼠标移出,第一张图在最后一张图右边
                        if (i == that.pics.length - 1) {
                            that.$refs.pic.children[0].style.transform = 'translate3d(440px,0px,0px)';
                        }
                        //自动轮播
                        this.time = setInterval(function() {
                            that.zindex++;
                            if (i == that.pics.length - 1) {
                                i = 0;
                            } else {
                                ++i
                            }
                            that.index = i;//必须储存index的值,否则值恒为0
                            that.$refs.pic.children[i].style.zIndex = that.zindex;
                            that.$refs.pic.children[i].style.transform = 'translate3d(0px,0px,0px)';
                            that.$refs.pic.children[i].style.transition = 'all 0.25s ease 0s';
                            that.pics[i].on = true;
                            //向左移动
                            if (i > 0) {
                                that.$refs.pic.children[i - 1].style.transition = 'all 0.55s ease 0s';
                                that.$refs.pic.children[i - 1].style.transform = 'translate3d(-440px,0px,0px)';
                                that.pics[i - 1].on = false;

                            } else {
                                that.$refs.pic.children[that.pics.length - 1].style.transition = 'all 0.55s ease 0s';
                                that.$refs.pic.children[that.pics.length - 1].style.transform = 'translate3d(-440px,0px,0px)';
                                that.pics[that.pics.length - 1].on = false;
                            }
                            //向右移动
                            if (i < that.pics.length - 1) {
                                that.$refs.pic.children[i + 1].style.transition = 'none 0s ease 0s';
                                that.$refs.pic.children[i + 1].style.transform = 'translate3d(440px,0px,0px)';
                            } else {
                                that.$refs.pic.children[0].style.transition = 'none 0s ease 0s';
                                that.$refs.pic.children[0].style.transform = 'translate3d(440px,0px,0px)';
                            }
                        }, 3000)
                    }
                },
                mounted: function() {
                    this.mouseout();
                    //初始状态,即0++++
                    for (let j = 1; j < this.pics.length; j++) {
                        this.$refs.pic.children[j].style.transform = 'translate3d(440px,0px,0px)'
                    }
                    this.$refs.pic.children[0].style.zIndex = '1';
                }
            });
        </script>
    </body>
</html>

这种轮播图比起上一种要复杂的多,但是看起来非常舒服,甚至比swiper的轮播图还要高档,给人的感觉就好像是两张图在相互切换,其他图在怎么变换根本看不出来。
根据我仔细的观察,分析出了原理,主要是通过translate3d变换位移,还需要加上叠放次序zindex,不然顺序会乱,你会看到其中一张图在过渡的时候压到其他图,这里的过渡要分开加,而不是写在CSS中,不是无脑加,第一张往最后一张图后面移动时不能有过渡动画,重点还要考虑第一张图和最后一张图的位置关系,这里一个过渡0.25s和一个过渡0.55s并不是没有道理,如果都设为0.55s可能会看到其它图的过渡惨杂进来
实现效果如图:
avatar