昨天在完善抢座程序的时候,遇到一个问题:当用户自由选择座位的时候,需要多级向下选择,比如先选择校区->选择房间->选择对应的座位,类似于发快递的时候选择收件地址一样,是一个多级选择效果。
在网上没有找到适合自己需求的选择器,有一个是基于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控件到自己手动创建控件的一个转折点,哈哈哈,开心。