美文网首页
自定义控件 - 基于swiper插件的底部多级选择器

自定义控件 - 基于swiper插件的底部多级选择器

作者: 木头就是我呀 | 来源:发表于2019-06-28 23:52 被阅读0次

昨天在完善抢座程序的时候,遇到一个问题:当用户自由选择座位的时候,需要多级向下选择,比如先选择校区->选择房间->选择对应的座位,类似于发快递的时候选择收件地址一样,是一个多级选择效果。
在网上没有找到适合自己需求的选择器,有一个是基于mui的,不太适合,所以基于这个需求,自己封装了一个比较简易的小控件以满足这里的需求。

控件的截图大致如下:没有做过多的样式效果,这个可以在每次使用的时候再自定义,现在加了效果很可能与以后的需求风格不匹配。


控件截图

大致的功能为 : 选择地区 -> 选择歌手 ->选择他的歌
这是一种常见的多级选择器的样式,可以应用在很多场景下。

接下来简单记录一下封装所对应的代码(部分代码):

首先是html代码:

<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
        <title>嗯.....</title>

        <link rel="stylesheet" href="css/swiper.min.css">
        <link rel="stylesheet" href="css/myPublic.css">
        
        <script src="js/jquery-1.10.2.js"></script>
        <script src="js/swiper.min.js"></script>
        <script src="js/mySelect.js"></script>

    </head>
    <body>
        <button class="updateData">更新数据</button>
        <div id="app" class="w750">
            <button class="btn_open">打开按钮</button>
            <button class="btn_close">关闭按钮</button>
            <!--选择器代码开始-->
            <div class="mt-select select1">
                <div class="select_box"></div>
            </div>
            <!--选择器代码结束-->
        </div>

        <script src="js/myIndex.js"></script>
    </body>
</html>

封装后对应的代码如下:

$(function() {
    var swiper_test;
    // data内部可以是字符串  也可以是键值对对象
    var data1 = [{key:'北京',value:1},{key:'西安',value:2},{key:'湖南',value:3}];
    var data2 = ['王菲','窦唯','宋冬野','赵雷','房东的猫','安九'];
    var data3 = ['无法长大', '不开的唇', '八十年代的歌', '十九岁', '已是两条路上的人', '阿刁'];
    $('.btn_open').on('click', function() {
        swiper_test = new selectSwiper({
            el: '.select1',
            colNum:3,
            data: data1,
            init: function(index) {
                // 初始化成功
            },
            updated: function(index){
                // 更新数据成功
                console.log("数据更新成功"+index);
            },
            mtSlideChangeEnd:function(swiper,activeIndex){
                // 移动停止
                // 列下标  swiper.myIndex
                // 滑动选择的下标  activeIndex

                // 简单用数据测试一下
                switch (swiper.myIndex) {
                    case 0:
                        // 第一列 滚动完毕 得到对应的值
                        if(activeIndex === -1){
                            return;
                        }
                        if(data1[activeIndex].value !== 1){
                            data2 = [];
                        }else{
                            data2 = ['王菲','窦唯','宋冬野','赵雷','房东的猫','安九'];
                        }
                        swiper_test.update_data(1,data2);
                        // 需要更新第三级
                        swiper_test.update_data(2,[]);
                        break;
                    case 1:
                        // 第二列 滚动完毕 得到对应的值
                        if(activeIndex === -1){
                            return;
                        }
                        if(data2[activeIndex] !== "赵雷"){
                            data3 = [];
                        }else{
                            data3 = ['无法长大', '不开的唇', '八十年代的歌', '十九岁', '已是两条路上的人', '阿刁'];
                        }
                        swiper_test.update_data(2,data3);
                        break;
                    case 2:
                        break;
                }
            },
            submit:function(data){
                // 提交成功
                swiper_test.close();
            },
            cancel:function(){
                // 取消提交
                console.log("取消成功");
            }
        });
        // 打开底部选择器
        swiper_test.open();
    });

    $(".btn_close").click(()=>{
        // 关闭底部选择器
        swiper_test.close();
    });

    $(".updateData").click(()=>{
        // 模拟更新数据
        var newData = ["111","222","333","444",];
        //  更新数据 - (待更新的列下标 , 更新的数据)
        swiper_test.update_data(2,newData);
    })
});

将swiper对应的原生方法进行了一些简单的封装,创建了一个selectSwiper对象,用户提供对应的一些参数和节点选择器就可以创建对象,使用open()方法打开,close()方法关闭,update_data()方法更新某一列的数据,并通过几个回调函数进行事件的捕捉:
init(初始化回调函数);
updated(数据更新完成回调函数);
mtSlideChangeEnd(滑动结束回调函数);
submit(数据提交成功回调函数);
cancel(取消提交回调函数)。

在这个层面上就可以很简单的创建这个底部选择器了,省去了很多繁琐的步骤,繁琐的步骤就是如下所示:

// 设置样式
(function(doc, win, page_width, font_size) {
    var docEl = doc.documentElement,
        resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
        recalc = function() {
            var clientWidth = docEl.clientWidth;
            if (!clientWidth) return;
            docEl.style.fontSize = clientWidth > page_width ? (font_size * 2) + 'px' : font_size * (clientWidth / (page_width /
                2)) + 'px';
        };
    if (!doc.addEventListener) return;
    win.addEventListener(resizeEvt, function() {
        recalc();
    }, false);
    doc.addEventListener('DOMContentLoaded', function() {
        recalc();
    }, false);
})(document, window, 750, 50);


// 对象 - 自定义
function selectSwiper(obj) {
    var _self = this;
    // 选择器
    _self.el = $(obj.el);
    // 底部列数
    _self.colNum =  obj.colNum || obj.data.length;
    // 自定义的swiperData对象
    _self.swiperData = {};
    // 初始化成功回调函数
    _self.swiperData.init = obj.init || function (){};
    // 用户传入的数据
    _self.swiperData.data = obj.data || [];
    // 用户手指滑动当前所选中的当前列的下标
    _self.swiperData.activeIndex = (typeof obj.activeIndex === 'number' && obj.activeIndex >= -1) ? obj.activeIndex : -1;
    // 自定义用于收集各个swiper对象
    _self.swiperData.mySwipperArray = [];
    // 回调函数 - 当滑动停止时
    _self.swiperData.mtSlideChangeEnd = obj.mtSlideChangeEnd || function(){};
    // 回调函数 - 用户点击提交按钮时
    _self.swiperData.submit = obj.submit || function(){};
    // 回调函数 - 用户点击取消按钮时
    _self.swiperData.cancel = obj.cancel || function(){};
    // 自定义对象 - 用户最后点击提交时所有滑动的下标
    _self.swiperData.resultIndexObj = {};
    // 回调函数 - 用户更新数据成功后
    _self.swiperData.updated = obj.updated || function(){};

    // 动态拼写上每一个子项的包裹代码
    _self.innerHtmlText = "";

    for(let i = 0;i<_self.colNum;i++){
        _self.innerHtmlText+=`
        <div class="select_box_item item`+i+`">
            <div class="selectData">
                <div class="cloth"></div>
                <div class="swiper-container">   
                    <div class="swiper-wrapper"></div>
                </div>
            </div>
        </div>
        `
    }

    /**
     * 初始化swiper
     */
    _self.init = function() {
        // 添加 “取消”、“确定” 按钮
        $(".mt-select"+_self.el.selector).prepend(`
            <div class="select-btn">
                <button class="btn-cancel">取消</button>
                <button class="btn-submit">确定</button>
            <div>`);
        // 添加遮罩层
        $("body").prepend('<div class="click_no_panel"></div>');
        // 将上面动态拼接的代码插入到dom中
        $(".mt-select"+_self.el.selector).find('.select_box').html(_self.innerHtmlText);
        // 动态为每一个子项内部赋值 - 用户传入的数据 - 进行解析 - 获得swiper对象 - 并收集
        for (let num = 0; num < this.colNum; num++) {
            // 为每个子项生成swiper对象
            var  mySwipper = new Swiper(_self.el.selector+" .item"+num+" .swiper-container", {
                direction: 'vertical',
                slidesPerView: 8,
                centeredSlides: true,
                slideToClickedSlide: true,
                onInit: function(swiper) {
                    // 获得当前列对应的数据
                    var data = _self.swiperData.data;
                    // 将生成的数据放入对象中
                    if(num === 0){
                        swiper.appendSlide(init_data_for_swiper(data));
                    }else{
                        swiper.appendSlide(init_data_for_swiper(undefined));
                    }
                },
                // 当滑动停止时的回调函数
                onSlideChangeEnd: function(swiper) {
                    // 得到当先选择器的下标 - 就可以知道是哪一个了
                    _self.swiperData.activeIndex = swiper.activeIndex - 1;
                    // 当前列的对应的某一项值
                    var activeIndex = _self.swiperData.activeIndex;
                    _self.swiperData.mtSlideChangeEnd(swiper,activeIndex);
                    // 滑动结束 - 将 {选择器下标 : 数据下标} 格式存入
                    _self.swiperData.resultIndexObj[swiper.myIndex]= activeIndex;
                },
            });
            // 初始化成功回调函数
            _self.swiperData.init(_self.swiperData.activeIndex);
            // 自定义的一个属性 - myIndex - 目的是为了能得到是哪一个项在滑动结束
            mySwipper.myIndex = num;
            // 收集到生成的对象
            this.swiperData.mySwipperArray.push(mySwipper);
        }
    }

    /**
     * 显示
     */
    _self.open = function(){
        var _self = this;
        $(".mt-select"+_self.el.selector).addClass('open');
        $(".mt-select"+_self.el.selector).addClass('open_ani');
        // 遮罩层点击事件
        $(".click_no_panel").click(()=>{
            // 关闭底部选择器
            _self.close();
        });
        // 初始化事件 - 取消
        $(".mt-select"+_self.el.selector).find(".btn-cancel").click(()=>{
            _self.close();
            // 调用取消的回调函数
            _self.swiperData.cancel();
        });
        // 初始化事件 - 提交
        $(".mt-select"+_self.el.selector).find(".btn-submit").click(()=>{
            // 调用提交的回调函数
            _self.swiperData.submit(_self.swiperData.resultIndexObj);
        });
    }

    /**
     * 关闭
     */
    _self.close = function() {
        // 点击遮罩层 - 去掉遮罩层
        $(".click_no_panel").remove();
        $(".mt-select"+_self.el.selector).removeClass('open');
        $(".mt-select"+_self.el.selector).removeClass('open_ani');

        // 清除掉对象 - 释放浏览器对其缓存
        for(let swiper of _self.swiperData.mySwipperArray){
            swiper.destroy(true);
        }
    };

    /**
     * 更新某一项的data数据
     * @param {Object} myIndex 项下标
     * @param {Object} data 数据本体
     */
    _self.update_data = function(myIndex,data){
        // 更新数据
        myIndex = myIndex || -1;
        data = data || [];
        if(myIndex === -1){
            throw "待更新的列下标不正确";
        }
        // 将待更新的列表的内部代码替换掉 - 否则会造成滑动有误
        let htmlStr =
            `<div class="selectData">
                <div class="cloth"></div>
                <div class="swiper-container">   
                    <div class="swiper-wrapper"></div>
                </div>
            </div>`;
        // 先清掉数据
        $(_self.el.selector+" .item"+myIndex).html(htmlStr);
        // 更新某个swiper的逻辑 - 即新建一个;对应的swiper对象
        var mySwipper = new Swiper(_self.el.selector+" .item"+myIndex+" .swiper-container", {
            direction: 'vertical',
            slidesPerView: 8,
            centeredSlides: true,
            slideToClickedSlide: true,
            onInit: function(swiper) {
                swiper.appendSlide(init_data_for_swiper(data));
                // 更新数据成功回调函数
                _self.swiperData.updated(myIndex);
            },
            onSlideChangeEnd: function(swiper) {
                // 得到当先选择器的下标 - 就可以知道是哪一个了
                _self.swiperData.activeIndex = swiper.activeIndex - 1;
                var activeIndex = _self.swiperData.activeIndex;
                _self.swiperData.mtSlideChangeEnd(swiper,activeIndex);
                // 滑动结束 - 将 {选择器下标 : 数据下标} 格式存入
                _self.swiperData.resultIndexObj[swiper.myIndex]= activeIndex;
            },
        });
        mySwipper.myIndex = myIndex;
        this.swiperData.mySwipperArray[myIndex] = mySwipper;
    };

    /**
     * 公用的方法
     * @param {Object} data 数据
     */
    function init_data_for_swiper(data){
        var s = [];
        s[0] = '<div class="swiper-slide">请选择</div>';
        if(data === undefined){
            return s;
        }
        for (i = 0; i < data.length; i++) {
            // 如果需要的是键值对 - 那么就在data中传入对象集合
            if(typeof data[i] === "object"){
                s[i + 1] = '<div class="swiper-slide" data="'+data[i].value+'">' + data[i].key + '</div>';
            }else{
                s[i + 1] = '<div class="swiper-slide">' + data[i] + '</div>';
            }
        }
        return s;
    }

    // 调用初始化方法
    _self.init();
}

以上代码为面向swiper封装的一些方法,比较简单,均可以在swiper官网查询到。

最后贴上对应的一些对应的css样式:

* {
   -webkit-box-sizing: border-box;
   -moz-box-sizing: border-box;
   box-sizing: border-box;
   appearance: none;
   -moz-appearance: none;
   -webkit-appearance: none
}
body{
   padding: 0;
   margin: 0;
}
.mt-select{
   position: fixed;
   height: 300px;
   width:100%;
   max-height: 400px;
   bottom: -300px;
   z-index: 11;
   background-color: white;
}
.select_box {
   /*伸缩布局*/
   display: flex;
   position: absolute;
   width:100%; 
   height: 250px;
   bottom:0;
}

.select_box .select_box_item {
   display: flex;
   height: 100%;
   flex: 1;
   flex-direction: column;
   align-items: center;
   justify-content: center;

   background-color: white;
   border: 1px solid rgba(100,100,100,0.1);
}


/* 字体大小 */
.select_box_item {
   font-size: 20px;
}

/* 打开时候的动画 */
.open{
   visibility: visible;
}
.open_ani{
   animation: fadeInUp .3s ease-out;
   animation-fill-mode: forwards
}
/* 遮罩层 */
.click_no_panel {
   width:100%;
   height: 100%;
   background-color:rgba(100,100,100,0.2);
   position: absolute;
   z-index: 10;
}

/* 确定-取消按钮 - 样式*/
.select-btn{
   height: 50px;
   width: 100%;
}
.select-btn button{
   position: absolute;
   width:50px;
   height: 30px;
   font-size: 15px;
   background-color: white;
   border: 0px;
   border-radius: 5px;
}
.select-btn button:active{
   background-color: rgba(100,100,100,0.2);
   color:white;
}
.select-btn .btn-cancel{
   left: 10px;
}
.select-btn .btn-cancel,.select-btn .btn-submit{
   top:10px;
}
.select-btn .btn-submit{
   right: 10px;
}

/* 滑动 - 效果 */
.swiper-container,
.selectData {
   height: 250px;
}

.swiper-slide {
   height: .7rem;
   line-height: .7rem;
   font-size: .4rem;
   color: #ccc;
   overflow: hidden;
   
   text-align: center;
}

.swiper-slide:first-child {
   color: #b7babf
}

.swiper-slide-prev,
.swiper-slide-next {
   font-size: .4rem;
}

.swiper-slide-active {
   font-size: .4rem;
   color: #191919
}

/* 选中行样式 */
.cloth {
   position: absolute;
   height: .625rem;
   top: 110px;
   left: 0;
   right: 0;
   background-color: rgba(100, 100, 100, .03)
}
/* 模拟更新数据 */
.updateData{
   position: absolute;
   z-index: 10000; 
}

@keyframes fadeInUp {
   0% {
       bottom: -300px;
   }
   85% {
       bottom: 10px;
   }
   100% {
       bottom: 0;
   }
}

以上是简单的做一些记录,完整代码在下面的github地址上面,以后有时间会持续更新完善,这是自己从网上copy控件到自己手动创建控件的一个转折点,哈哈哈,开心。

github地址:
https://github.com/xicunyang/bottom-select/

相关文章

网友评论

      本文标题:自定义控件 - 基于swiper插件的底部多级选择器

      本文链接:https://www.haomeiwen.com/subject/kwvycctx.html