/*! * mobileSelect.js * (c) 2017-present onlyhom * Released under the MIT License. */ (function(window) { // 获取指定元素的类名 function getClass(dom, string) { return dom.getElementsByClassName(string); } // 构造器 function MobileSelect(config) { this.mobileSelect; // DOM元素,表示整个选择器 this.wheelsData = config.wheels; // 滚轮数据 this.jsonType = false; // 是否为JSON类型数据 this.cascadeJsonData = []; // 级联JSON数据 this.displayJson = []; // 显示的JSON数据 this.curValue = null; // 当前值 this.curIndexArr = []; // 当前索引数组 this.startY; // 起始Y坐标 this.moveEndY; // 结束Y坐标 this.moveY; // 移动Y坐标 this.oldMoveY; // 旧的移动Y坐标 this.offset = 0; // 偏移量 this.offsetSum = 0; // 偏移总和 this.oversizeBorder; // 超出边界大小 this.curDistance = []; // 当前距离数组 this.clickStatus = false; // 点击状态 this.isPC = true; // 是否为PC端 this.init(config); } // 原型链 MobileSelect.prototype = { constructor: MobileSelect, // 初始化函数 init: function(config) { var _this = this; _this.keyMap = config.keyMap ? config.keyMap : { id: 'id', value: 'value', childs: 'childs' }; // 键值映射 _this.checkDataType(); // 检查数据类型 _this.renderWheels(_this.wheelsData, config.cancelBtnText, config.ensureBtnText); // 渲染滚轮 _this.trigger = document.querySelector(config.trigger); // 触发器元素 if (!_this.trigger) { console.error('mobileSelect has been successfully installed, but no trigger found on your page.'); return false; } _this.wheel = getClass(_this.mobileSelect, 'wheel'); // 获取滚轮元素 _this.slider = getClass(_this.mobileSelect, 'selectContainer'); // 获取滑块容器 _this.wheels = _this.mobileSelect.querySelector('.wheels'); // 获取所有滚轮的容器 _this.liHeight = _this.mobileSelect.querySelector('li').offsetHeight; // 获取每个选项的高度 _this.ensureBtn = getClass(_this.mobileSelect, 'ensure'); // 确保按钮 _this.cancelBtn = getClass(_this.mobileSelect, 'cancel'); // 取消按钮 _this.grayLayer = getClass(_this.mobileSelect, 'grayLayer'); // 灰色遮罩层 _this.popUp = getClass(_this.mobileSelect, 'content'); // 弹出层 _this.callback = config.callback ? config.callback : function() {}; // 回调函数 _this.cancel = config.cancel ? config.cancel : function() {}; // 取消函数 _this.transitionEnd = config.transitionEnd ? config.transitionEnd : function() {}; // 过渡结束函数 _this.titleText = config.title ? config.title : ''; // 标题文本 _this.connector = config.connector ? config.connector : ' '; // 连接符 _this.triggerDisplayData = !(typeof(config.triggerDisplayData) == 'undefined') ? config.triggerDisplayData : true; // 是否显示在触发器上 _this.trigger.style.cursor = 'pointer'; // 设置触发器的鼠标样式为指针 _this.setStyle(config); // 设置样式 _this.setTitle(_this.titleText); // 设置标题 _this.checkIsPC(); // 检查是否是PC端 _this.checkCascade(); // 检查是否需要级联 if (_this.cascade) { _this.initCascade(); // 初始化级联 } // 定位初始位置 if (_this.initPosition.length < _this.slider.length) { var diff = _this.slider.length - _this.initPosition.length; for (var i = 0; i < diff; i++) { _this.initPosition.push(0); } } _this.setCurDistance(_this.initPosition); // 设置当前距离 _this.addListenerAll(); // 添加事件监听器 _this.fixRowStyle(); // 修正列数 }, // 设置标题 setTitle: function(string) { var _this = this; _this.titleText = string; _this.mobileSelect.querySelector('.title').innerHTML = _this.titleText; }, // 设置样式 setStyle: function(config) { var _this = this; if (config.ensureBtnColor) { _this.ensureBtn.style.color = config.ensureBtnColor; // 确保按钮颜色 } if (config.cancelBtnColor) { _this.cancelBtn.style.color = config.cancelBtnColor; // 取消按钮颜色 } if (config.titleColor) { _this.title = _this.mobileSelect.querySelector('.title'); // 标题元素 _this.title.style.color = config.titleColor; // 标题颜色 } if (config.textColor) { _this.panel = _this.mobileSelect.querySelector('.panel'); // 面板元素 _this.panel.style.color = config.textColor; // 文本颜色 } if (config.titleBgColor) { _this.btnBar = _this.mobileSelect.querySelector('.btnBar'); // 按钮栏元素 _this.btnBar.style.backgroundColor = config.titleBgColor; // 按钮栏背景颜色 } if (config.bgColor) { _this.panel = _this.mobileSelect.querySelector('.panel'); // 面板元素 _this.shadowMask = _this.mobileSelect.querySelector('.shadowMask'); // 阴影遮罩层元素 _this.panel.style.backgroundColor = config.bgColor; // 面板背景颜色 _this.shadowMask.style.background = 'linear-gradient(to bottom, ' + config.bgColor + ', rgba(255, 255, 255, 0), ' + config.bgColor + ')'; // 阴影遮罩层渐变背景 } }, // 检查是否是PC端 checkIsPC: function() { var _this = this; var sUserAgent = navigator.userAgent.toLowerCase(); var bIsIpad = sUserAgent.match(/ipad/i) == "ipad"; // 判断是否为iPad var bIsIphoneOs = sUserAgent.match(/iphone os/i) == "iphone os"; // 判断是否为iPhone OS var bIsMidp = sUserAgent.match(/midp/i) == "midp"; // 判断是否为MIDP设备 var bIsUc7 = sUserAgent.match(/rv:1.2.3.4/i) == "rv:1.2.3.4"; // 判断是否为UC7浏览器 var bIsUc = sUserAgent.match(/ucweb/i) == "ucweb"; // 判断是否为UC浏览器 var bIsAndroid = sUserAgent.match(/android/i) == "android"; // 判断是否为Android设备 var bIsCE = sUserAgent.match(/windows ce/i) == "windows ce"; // 判断是否为Windows CE设备 var bIsWM = sUserAgent.match(/windows mobile/i) == "windows mobile"; // 判断是否为Windows Mobile设备 if ((bIsIpad || bIsIphoneOs || bIsMidp || bIsUc7 || bIsUc || bIsAndroid || bIsCE || bIsWM)) { _this.isPC = false; } }, show: function(){ // 显示选择器,通过添加类名来控制显示 this.mobileSelect.classList.add('mobileSelect-show'); }, renderWheels: function(wheelsData, cancelBtnText, ensureBtnText){ var _this = this; var cancelText = cancelBtnText ? cancelBtnText : '取消'; // 设置取消按钮文本 var ensureText = ensureBtnText ? ensureBtnText : '确认'; // 设置确认按钮文本 _this.mobileSelect = document.createElement("div"); // 创建选择器的容器元素 _this.mobileSelect.className = "mobileSelect"; // 设置容器的类名 _this.mobileSelect.innerHTML = '
'+ '
'+ '
'+ '
'+ '
'+ cancelText +'
'+ '
'+ '
'+ ensureText +'
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'+ '
'; document.body.appendChild(_this.mobileSelect); // 将选择器添加到页面中 // 根据数据长度来渲染轮盘 var tempHTML=''; for(var i=0; i'+wheelsData[i].data[j][_this.keyMap.value]+''; // 如果是JSON类型,使用键值对填充列表项 } }else{ for(var j=0; j'; // 如果不是JSON类型,直接使用数据填充列表项 } } tempHTML += ''; // 结束当前轮盘 } _this.mobileSelect.querySelector('.wheels').innerHTML = tempHTML; // 将生成的HTML添加到轮盘容器中 }, addListenerAll: function(){ var _this = this; for(var i=0; i<_this.slider.length; i++){ // 为每个滑块添加手势监听和点击监听 (function (i) { _this.addListenerWheel(_this.wheel[i], i); _this.addListenerLi(i); })(i); } }, addListenerWheel: function(theWheel, index){ var _this = this; theWheel.addEventListener('touchstart', function () { _this.touch(event, this.firstChild, index); // 触摸开始事件处理 },false); theWheel.addEventListener('touchend', function () { _this.touch(event, this.firstChild, index); // 触摸结束事件处理 },false); theWheel.addEventListener('touchmove', function () { _this.touch(event, this.firstChild, index); // 触摸移动事件处理 },false); if(_this.isPC){ // 如果是PC端则再增加拖拽监听,方便调试 theWheel.addEventListener('mousedown', function () { _this.dragClick(event, this.firstChild, index); // 鼠标按下事件处理 },false); theWheel.addEventListener('mousemove', function () { _this.dragClick(event, this.firstChild, index); // 鼠标移动事件处理 },false); theWheel.addEventListener('mouseup', function () { _this.dragClick(event, this.firstChild, index); // 鼠标抬起事件处理 },true); } }, addListenerLi: function(sliderIndex) { // 获取当前对象引用 var _this = this; // 获取指定滑块索引的所有列表项元素 var curWheelLi = _this.slider[sliderIndex].getElementsByTagName('li'); // 遍历所有列表项,为每个列表项添加点击事件监听器 for (var j = 0; j < curWheelLi.length; j++) { (function(j) { curWheelLi[j].addEventListener('click', function() { // 当列表项被点击时,调用singleClick方法处理点击事件 _this.singleClick(this, j, sliderIndex); }, false); })(j); } }, checkDataType: function() { // 获取当前对象引用 var _this = this; // 检查wheelsData中第一个数据项是否为对象类型 if (typeof(_this.wheelsData[0].data[0]) == 'object') { _this.jsonType = true; // 如果是对象类型,设置jsonType为true } }, checkCascade: function() { // 获取当前对象引用 var _this = this; // 如果数据类型为JSON if (_this.jsonType) { var node = _this.wheelsData[0].data; // 遍历数据节点,检查是否存在子节点 for (var i = 0; i < node.length; i++) { if (_this.keyMap.childs in node[i] && node[i][_this.keyMap.childs].length > 0) { _this.cascade = true; // 如果存在子节点,设置级联标志为true _this.cascadeJsonData = _this.wheelsData[0].data; // 保存级联数据 break; // 找到第一个有子节点的节点后退出循环 } } } else { _this.cascade = false; // 如果数据类型不是JSON,设置级联标志为false } }, generateArrData: function(targetArr) { var tempArr = []; // 初始化临时数组用于存储转换后的数据 var keyMap_id = this.keyMap.id; // 获取键映射中的ID键名 var keyMap_value = this.keyMap.value; // 获取键映射中的值键名 // 遍历目标数组,将每个元素转换为包含ID和值的对象 for (var i = 0; i < targetArr.length; i++) { var tempObj = {}; tempObj[keyMap_id] = targetArr[i][this.keyMap.id]; tempObj[keyMap_value] = targetArr[i][this.keyMap.value]; tempArr.push(tempObj); } return tempArr; // 返回转换后的数组 }, initCascade: function() { // 获取当前对象引用 var _this = this; // 生成并添加级联数据的数组表示形式到displayJson中 _this.displayJson.push(_this.generateArrData(_this.cascadeJsonData)); // 如果初始化位置数组不为空,则进行深度初始化 if (_this.initPosition.length > 0) { _this.initDeepCount = 0; // 初始化深度计数器 _this.initCheckArrDeep(_this.cascadeJsonData[_this.initPosition[0]]); // 递归初始化子节点 } else { _this.checkArrDeep(_this.cascadeJsonData[0]); // 如果初始化位置为空,直接检查第一层数据 } _this.reRenderWheels(); // 重新渲染轮盘组件 }, initCheckArrDeep: function(parent) { // 获取当前对象引用 var _this = this; // 如果父节点存在且具有子节点 if (parent) { if (_this.keyMap.childs in parent && parent[_this.keyMap.childs].length > 0) { // 生成并添加子节点数据的数组表示形式到displayJson中 _this.displayJson.push(_this.generateArrData(parent[_this.keyMap.childs])); _this.initDeepCount++; // 增加深度计数器 var nextNode = parent[_this.keyMap.childs][_this.initPosition[_this.initDeepCount]]; // 获取下一个节点 if (nextNode) { _this.initCheckArrDeep(nextNode); // 递归初始化下一个节点 } else { _this.checkArrDeep(parent[_this.keyMap.childs][0]); // 如果下一个节点不存在,检查第一个子节点 } } } }, checkArrDeep: function (parent) { // 检测子节点深度并修改 displayJson var _this = this; if(parent){ if (_this.keyMap.childs in parent && parent[_this.keyMap.childs].length > 0) { _this.displayJson.push(_this.generateArrData(parent[_this.keyMap.childs])); // 生成子节点数组 _this.checkArrDeep(parent[_this.keyMap.childs][0]); // 检测下一个子节点 } } }, checkRange: function(index, posIndexArr){ var _this = this; var deleteNum = _this.displayJson.length - 1 - index; for(var i=0; i posIndexArr.length){ tempCount = _this.slider.length - posIndexArr.length; for(var i=0; i _this.displayJson.length){ var count = _this.wheel.length - _this.displayJson.length; for(var i=0; i'+_this.displayJson[i][j][_this.keyMap.value]+''; } _this.slider[i].innerHTML = tempHTML; // 更新现有wheel的HTML内容 }else{ var tempWheel = document.createElement("div"); tempWheel.className = "wheel"; tempHTML = '
    '; for(var j=0; j<_this.displayJson[i].length; j++){ // 行 tempHTML += '
  • '+_this.displayJson[i][j][_this.keyMap.value]+'
  • '; } tempHTML += '
'; tempWheel.innerHTML = tempHTML; // 创建新的wheel并设置HTML内容 _this.addListenerWheel(tempWheel, i); // 为新wheel添加事件监听器 _this.wheels.appendChild(tempWheel); // 将新wheel添加到DOM中 } _this.addListenerLi(i); // 为每个li添加事件监听器 })(i); } }, updateWheels: function(data) { var _this = this; if (_this.cascade) { // 更新级联数据源 _this.cascadeJsonData = data; _this.displayJson = []; _this.initCascade(); // 确保初始化位置数组长度与滑块数量一致 if (_this.initPosition.length < _this.slider.length) { var diff = _this.slider.length - _this.initPosition.length; for (var i = 0; i < diff; i++) { _this.initPosition.push(0); } } _this.setCurDistance(_this.initPosition); _this.fixRowStyle(); } }, updateWheel: function(sliderIndex, data) { var _this = this; var tempHTML = ''; if (_this.cascade) { console.error('级联格式不支持updateWheel(),请使用updateWheels()更新整个数据源'); return false; } else if (_this.jsonType) { // 处理JSON类型的数据 for (var j = 0; j < data.length; j++) { tempHTML += '
  • ' + data[j][_this.keyMap.value] + '
  • '; } _this.wheelsData[sliderIndex] = { data: data }; } else { // 处理普通数组类型的数据 for (var j = 0; j < data.length; j++) { tempHTML += '
  • ' + data[j] + '
  • '; } _this.wheelsData[sliderIndex] = data; } _this.slider[sliderIndex].innerHTML = tempHTML; _this.addListenerLi(sliderIndex); }, fixRowStyle: function() { var _this = this; var width = (100 / _this.wheel.length).toFixed(2); for (var i = 0; i < _this.wheel.length; i++) { _this.wheel[i].style.width = width + '%'; } }, getIndex: function(distance) { return Math.round((2 * this.liHeight - distance) / this.liHeight); }, getIndexArr: function() { var _this = this; var temp = []; for (var i = 0; i < _this.curDistance.length; i++) { temp.push(_this.getIndex(_this.curDistance[i])); } return temp; }, getCurValue: function() { var _this = this; var temp = []; var positionArr = _this.getIndexArr(); if (_this.cascade) { for (var i = 0; i < _this.wheel.length; i++) { temp.push(_this.displayJson[i][positionArr[i]]); } } else if (_this.jsonType) { for (var i = 0; i < _this.curDistance.length; i++) { temp.push(_this.wheelsData[i].data[_this.getIndex(_this.curDistance[i])]); } } else { for (var i = 0; i < _this.curDistance.length; i++) { temp.push(_this.getInnerHtml(i)); } } return temp; }, getValue: function() { return this.curValue; }, calcDistance: function(index){ // 计算给定索引对应的距离,基于liHeight的倍数 return 2*this.liHeight-index*this.liHeight; }, setCurDistance: function(indexArr){ var _this = this; var temp = []; for(var i=0; i<_this.slider.length; i++){ temp.push(_this.calcDistance(indexArr[i])); // 计算每个滑块的距离并存储在临时数组中 _this.movePosition(_this.slider[i],temp[i]); // 移动滑块到计算出的位置 } _this.curDistance = temp; // 更新当前距离数组 }, fixPosition: function(distance){ // 根据距离修正位置,确保滑块不会超出边界 return -(this.getIndex(distance)-2)*this.liHeight; }, movePosition: function(theSlider, distance){ // 使用CSS transform属性移动滑块到指定位置 theSlider.style.webkitTransform = 'translate3d(0,' + distance + 'px, 0)'; theSlider.style.transform = 'translate3d(0,' + distance + 'px, 0)'; }, locatePosition: function(index, posIndex){ // 定位滑块到特定位置 this.curDistance[index] = this.calcDistance(posIndex); this.movePosition(this.slider[index],this.curDistance[index]); if(_this.cascade){ _this.checkRange(index, _this.getIndexArr()); // 如果启用级联,检查范围 } }, updateCurDistance: function(theSlider, index){ // 更新当前滑块的距离 this.curDistance[index] = parseInt(theSlider.style.transform.split(',')[1]); }, getDistance:function(theSlider){ // 获取滑块当前的垂直距离 return parseInt(theSlider.style.transform.split(',')[1]); }, getInnerHtml: function(sliderIndex){ // 获取滑块当前选中项的内部HTML内容 var _this = this; var index = _this.getIndex(_this.curDistance[sliderIndex]); return _this.slider[sliderIndex].getElementsByTagName('li')[index].innerHTML; }, touch: function(event, theSlider, index) { var _this = this; event = event || window.event; switch (event.type) { case "touchstart": _this.startY = event.touches[0].clientY; // 记录触摸开始时的Y坐标 _this.oldMoveY = _this.startY; // 初始化旧的移动Y坐标 break; case "touchend": _this.moveEndY = event.changedTouches[0].clientY; // 记录触摸结束时的Y坐标 _this.offsetSum = _this.moveEndY - _this.startY; // 计算总偏移量 // 修正位置 _this.updateCurDistance(theSlider, index); _this.curDistance[index] = _this.fixPosition(_this.curDistance[index]); _this.movePosition(theSlider, _this.curDistance[index]); _this.oversizeBorder = -(theSlider.getElementsByTagName('li').length - 3) * _this.liHeight; // 反弹效果处理 if (_this.curDistance[index] + _this.offsetSum > 2 * _this.liHeight) { _this.curDistance[index] = 2 * _this.liHeight; setTimeout(function () { _this.movePosition(theSlider, _this.curDistance[index]); }, 100); } else if (_this.curDistance[index] + _this.offsetSum < _this.oversizeBorder) { _this.curDistance[index] = _this.oversizeBorder; setTimeout(function () { _this.movePosition(theSlider, _this.curDistance[index]); }, 100); } _this.transitionEnd(_this.getIndexArr(), _this.getCurValue()); // 触发过渡结束事件 if (_this.cascade) { _this.checkRange(index, _this.getIndexArr()); // 如果启用级联,检查范围 } break; // 处理鼠标抬起事件 case "mouseup": _this.moveEndY = event.clientY; // 获取鼠标抬起时的Y坐标 _this.offsetSum = _this.moveEndY - _this.startY; // 计算鼠标移动的距离 // 修正位置 _this.updateCurDistance(theSlider, index); // 更新当前距离 _this.curDistance[index] = _this.fixPosition(_this.curDistance[index]); // 修正位置,防止超出边界 _this.movePosition(theSlider, _this.curDistance[index]); // 移动到新的位置 _this.oversizeBorder = -(theSlider.getElementsByTagName('li').length - 3) * _this.liHeight; // 设置超出边界的最小值 // 反弹效果 if (_this.curDistance[index] + _this.offsetSum > 2 * _this.liHeight) { _this.curDistance[index] = 2 * _this.liHeight; // 如果超过上界,设置为上界 setTimeout(function () { _this.movePosition(theSlider, _this.curDistance[index]); // 延迟后移动到上界位置 }, 100); } else if (_this.curDistance[index] + _this.offsetSum < _this.oversizeBorder) { _this.curDistance[index] = _this.oversizeBorder; // 如果超过下界,设置为下界 setTimeout(function () { _this.movePosition(theSlider, _this.curDistance[index]); // 延迟后移动到下界位置 }, 100); } _this.clickStatus = false; // 重置点击状态 _this.transitionEnd(_this.getIndexArr(), _this.getCurValue()); // 触发过渡结束事件 if (_this.cascade) { _this.checkRange(index, _this.getIndexArr()); // 如果是级联选择器,检查范围 } break; // 处理鼠标移动事件 case "mousemove": event.preventDefault(); // 阻止默认行为 if (_this.clickStatus) { _this.moveY = event.clientY; // 获取当前鼠标Y坐标 _this.offset = _this.moveY - _this.oldMoveY; // 计算偏移量 _this.updateCurDistance(theSlider, index); // 更新当前距离 _this.curDistance[index] = _this.curDistance[index] + _this.offset; // 更新当前距离 _this.movePosition(theSlider, _this.curDistance[index]); // 移动到新的位置 _this.oldMoveY = _this.moveY; // 更新旧的Y坐标 } break; } }, // 处理单击事件 singleClick: function (theLi, index, sliderIndex) { var _this = this; if (_this.cascade) { var tempPosArr = _this.getIndexArr(); // 获取当前索引数组 tempPosArr[sliderIndex] = index; // 更新指定滑块的索引 _this.checkRange(sliderIndex, tempPosArr); // 检查范围 } else { _this.curDistance[sliderIndex] = (2 - index) * _this.liHeight; // 计算新的距离 _this.movePosition(theLi.parentNode, _this.curDistance[sliderIndex]); // 移动到新的位置 } } }; if (typeof exports == "object") { module.exports = MobileSelect; // CommonJS模块导出 } else if (typeof define == "function" && define.amd) { define([], function () { return MobileSelect; // AMD模块定义 }) } else { window.MobileSelect = MobileSelect; // 全局变量导出 } })();