美文网首页
自定义控件 - 基于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