dlmh_system/VRS/Assets/mobile-picker/mobile-picker.js

339 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;(function(global){
var PICKERCOUNT = 0;
var body = document.getElementsByTagName("body")[0];
var coordinate = {start: {y:0},end: {y:0,status:true}, move: {y:0}};
var Util = {
removeClass: function(el, className) {
var reg = new RegExp('(\\s|^)' + className + '(\\s|$)');
el.className = el.className.replace(reg, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, '');
},
addClass: function(el, className) {
!Util.hasClass(el,className) && (el.className += (el.className ? ' ' : '') + className);
},
hasClass: function(el, className) {
return !!el.className && new RegExp('(^|\\s)' + className + '(\\s|$)').test(el.className);
},
loop: function(start,end,handle){
for(var i = start; i < end; i++){
Util.isFunc(handle) && handle(i);
}
},
isFunc: function(name){
return typeof name === 'function';
},
isArray: function(o) {
return Object.prototype.toString.call(o) === '[object Array]';
},
isObject: function(o) {
return typeof o === 'object';
},
damping: function (value) {//阻尼运算
var steps = [20, 40, 60, 80, 100],rates = [0.5, 0.4, 0.3, 0.2, 0.1];
var result = value,len = steps.length;
while (len--) {
if (value > steps[len]) {
result = (value - steps[len]) * rates[len];
for (var i = len; i > 0; i--) {
result += (steps[i] - steps[i - 1]) * rates[i - 1];
}
result += steps[0] * 1;
break;
}
}
return result;
},
createEle: function(parent, tag, className, html) {
var ele = document.createElement(tag);
className && Util.addClass(ele,className);
html && (ele.innerHTML = html);
parent && parent.appendChild(ele);
return ele;
},
getEle: function(ctx, selector) {
return ctx.querySelector(selector);
},
setTransform: function(el,y) {
el.style.transform = 'translate3d(0,'+ y +'px,0)';
}
}
function Picker(config){
this.index = ++PICKERCOUNT;//当前选择器的索引
this.target = config.target instanceof HTMLElement ? config.target : typeof config.target === "string" ? Util.getEle(document,config.target) : null;//触发选择器的dom元素
this.data = config.data || [];//需要显示的数据
this.value = config.value ? (Util.isArray(config.value) ? config.value : config.value.split(',')) : [];//选择器默认值
this.childKey = config.childKey || 'child';//子数据索引名
this.valueKey = config.valueKey || 'value';//用于索引初始值的key
this.textKey = config.textKey || 'value';//用于显示的key
this.autoFill = !(config.autoFill === false);//选择确定后是否填充到目标元素
this.confirm = config.confirm;//确定选择的回调
this.cancel = config.cancel;//取消回调
this.initCallback = config.initCallback;//实例化完成的回调
this.select = config.select;//单个列表选择后的回调
this.lock = config.lock === true;//锁定确定按钮,用于异步加载时等待使用
this.className = config.className || '';//定制的类名
this.init();
}
Picker.prototype = {
constructor: Picker,
init: function(){
this.initResult();
var html = '<div class="mp-container"><div class="mp-header"><span class="mp-cancel">取消</span><span class="mp-confirm'+(this.lock ? ' disabled' : '')+'">确定</span></div><div class="mp-content"><div class="mp-shadowup"></div><div class="mp-shadowdown"></div><div class="mp-line"></div><div class="mp-box"></div></div>';
var container = Util.createEle(body,'div','mp-mask',html);
this.className && Util.addClass(container,this.className);
container.id = 'mobilePicker'+this.index;
this.container = container;
this.box = Util.getEle(container, '.mp-box')//用于包含滚动元素的容器
this.createScroll(this.data);//核心方法:创建滚动的元素
this.value = [];
this.bindEvent();//绑定事件
this.finisInit();
},
initResult: function(config){
this.scrollCount = 0;//已渲染的数据层级数
this.selectIndex = [];//每个层级选中的索引集合
this.result = [];//选择器最终的结果
this.offset = [];//每个层级滚动的偏移量集合
},
finisInit: function(){
var value = this.fillResult();
Util.isFunc(this.initCallback) && this.initCallback(value,this.result);
},
update: function(options){
for(var i in options) {
this[i] = options[i];
}
this.initResult()
this.box.innerHTML = '';
this.createScroll(this.data);
this.value = [];
},
getData: function(indexes){//获取数据集合
var arr = [];
for(var i = 0; i < indexes.length; i++){
arr = i == 0 ? this.data : arr[indexes[i -1]][this.childKey];
}
return arr;
},
setResult: function(data){
if(!Util.isObject(data)) return data;
var temp = {};
for(var key in data){
key != this.childKey && (temp[key] = data[key]);
}
return temp;
},
createScroll: function(data){
var scroll = Util.createEle(this.box,'div','mp-list-wrap','<ul></ul>');
scroll.scrollIndex = this.scrollCount++;
this.addList(Util.getEle(scroll, 'ul'), data);
},
getText: function(data){
return Util.isObject(data) ? data[this.textKey] : data;
},
addList: function(parent, data){
var html = '',that = this;
var index = 0,scrollIndex = parent.parentNode.scrollIndex,text = '';
Util.loop(0,data.length,function(i){
text = that.getText(data[i]);
html += '<li>'+text+'</li>';
//初始化时有默认值应该选中当前值否则index就会为0即选中第一个
if(that.value.length && that.value[scrollIndex] && (Util.isObject(data[i]) && data[i][that.valueKey] == that.value[scrollIndex][that.valueKey] || data[i] == that.value[scrollIndex])){
index = i;
}
});
parent.innerHTML = html;
this.offset.push(0);
this.selectItem(data, index, scrollIndex);//选中并创建下一级选择器
},
updateList: function(index,data){
var dom = this.box.childNodes[index];
if(!dom){
this.createScroll(data);
return;
}
dom = Util.getEle(dom,'ul');
this.addList(dom, data);
},
setScroll: function(index,data,value,callback) {
value && (this.value[index] = value);
this.offset.length = this.selectIndex.length = this.result.length = this.selectIndex.length = index;
if(index == 0){
this.data = data;
} else {
var temp = this.data[this.selectIndex[0]];
for(var i = 1, len = index; i < len; i++){
temp = temp[this.childKey][this.selectIndex[i]];
}
temp && (temp[this.childKey] = data);
}
this.updateList(index,data);
this.value = [];
Util.isFunc(callback) && callback(index,this.result);
},
removeScroll: function(index){
var that = this;
var node = this.box.childNodes[index];
if(node){
this.box.removeChild(node);
this.scrollCount--;
this.calcWidth();
}
},
calcWidth: function() {
var wraps = this.box.querySelectorAll('.mp-list-wrap');
for(var m = 0; m < wraps.length; m++){
wraps[m].style.width = (100 / this.scrollCount) + '%';
}
},
selectItem:function(data, index, scrollIndex){//params: 数据选中的索引当前scroll的索引
var that = this;
var oldScrollCount = this.scrollCount;
this.selectIndex.length = this.result.length = scrollIndex + 1;
this.selectIndex[scrollIndex] = index;
this.result[scrollIndex] = this.setResult(data[index]);
this.setOffset(scrollIndex, index);
if(data[index] && data[index][that.childKey] && Util.isArray(data[index][that.childKey]) && data[index][that.childKey].length){//存在子元素
if(that.scrollCount < scrollIndex + 2){//如果上一次的ul个数少于当前需要的个数则创建新的ul
that.createScroll(data[index][that.childKey]);
} else {
that.updateList(scrollIndex + 1, data[index][that.childKey]);
}
} else {//说明当前的滚动器数目多于需要的,移除多余的
for ( var j = oldScrollCount - 1, len = that.selectIndex.length; j >= len; j-- ) {//删除多余的ul
that.removeScroll(j);
}
}
// this.scrollIndex = this.offset.length = this.selectIndex.length;
this.offset.length = this.selectIndex.length;
this.calcWidth();//计算滚动对象的宽度
Util.isFunc(that.select) && that.select(scrollIndex,this.result,index,data[index] && data[index][that.childKey] && Util.isArray(data[index][that.childKey]) && data[index][that.childKey].length);
},
fillContent: function(content){
var tagName = this.target.tagName.toLowerCase();
if(['input','select','textarea'].indexOf(tagName) != -1) {
this.target.value = content;
} else {
this.target.innerText = content;
}
},
fillResult: function(){
var value = '';
for(var i = 0,len = this.result.length; i < len; i++){
if(Util.isObject(this.result[i])){
this.result[i][this.textKey] && (value += this.result[i][this.textKey]);
} else {
value += this.result[i];
}
}
this.autoFill && this.fillContent(value);
return value;
},
hide: function(){
var that = this;
Util.getEle(this.container,'.mp-container').style.transform = 'translate3d(0,100%,0)';
Util.removeClass(body, 'mp-body');
setTimeout(function(){
that.container.style.visibility = 'hidden';
},250)
},
show: function(){
var that = this;
that.container.style.visibility = 'visible';
Util.addClass(body, 'mp-body');
setTimeout(function(){
Util.getEle(that.container,'.mp-container').style.transform= 'translate3d(0,0,0)';
},0)
},
setOffset: function(scrollIndex, index){
var scroll = this.box.childNodes[scrollIndex].querySelector('ul');
var offset = scroll.childNodes[0] ? scroll.childNodes[0].offsetHeight * index : 0;
Util.setTransform(scroll, -offset)
this.offset[scrollIndex] = offset;
},
setLock: function(value){
var confirm = Util.getEle(this.container,'.mp-confirm'),old = this.lock;
this.lock = value !== false;
if(old !== this.lock) {
this.lock ? Util.addClass(confirm,'disabled') : Util.removeClass(confirm, 'disabled');
}
},
bindEvent: function(){
var that = this;
that.target.disabled = true;
['touchstart','touchend','touchmove'].forEach(function(action){
that.box.parentNode.addEventListener(action,function(event){
event = event || window.event;
event.preventDefault();
var target = event.target;
var index = target.parentNode.scrollIndex;
var child = target.childNodes;
var liHeight = child[child.length - 1].offsetHeight;
var scrollHeight = child[child.length - 2].offsetTop;
if(target.tagName.toLowerCase() != 'ul') return;
switch(action) {
case 'touchstart':
if(coordinate.end.status){
coordinate.end.status = !coordinate.end.status;
coordinate.start.y = event.touches[0].clientY;
coordinate.start.time = Date.now();
}
break;
case 'touchmove':
coordinate.move.y = event.touches[0].clientY;
var distance = coordinate.start.y - coordinate.move.y;
var os = distance + that.offset[index];
if(os < 0){//已经滑到最顶部
Util.setTransform(target, Util.damping(-os));
} else if(os <= scrollHeight){
Util.setTransform(target, -os);
} else {//超过了整体的高度
Util.setTransform(target, -(scrollHeight + Util.damping(os-scrollHeight)));
}
break;
case 'touchend':
coordinate.end.y = event.changedTouches[0].clientY;
var count = Math.floor((that.offset[index] + (coordinate.start.y - coordinate.end.y))/liHeight + 0.5)
count = count < 0 ? 0 : Math.min(count, target.childNodes.length - 1);
var temp = that.offset[index];
that.offset[index] = count < 0 ? 0 : Math.min(count * liHeight,target.offsetHeight - 5 * liHeight)
Util.setTransform(target, -that.offset[index]);
coordinate.end.status = true;
that.selectIndex.length = index + 1;
that.selectIndex[index] = count;
that.selectItem(that.getData(that.selectIndex),count,index)
break;
}
},false)
});
that.target.addEventListener('touchstart',function(event){
(event || window.event).preventDefault();
//记录旧结果,用于取消恢复
that.oldResult = that.result.slice(0);
that.show();
});
// 用click事件代替touchstart防止点透
Util.getEle(that.container,'.mp-cancel').addEventListener('click',function(){
that.hide();
//恢复旧的结果
that.update({
value: that.oldResult,
valueKey: that.textKey
});
Util.isFunc(that.cancel) && that.cancel();
},false);
Util.getEle(that.container,'.mp-confirm').addEventListener('click',function(){
if(that.lock) return;
var value = that.fillResult();
that.hide();
Util.isFunc(that.confirm) && that.confirm(value, that.result);
});
}
}
if (typeof module !== 'undefined' && typeof exports === 'object') {
module.exports = Picker;
} else if (typeof define === 'function' && (define.amd || define.cmd)) {
define(function() { return Picker; });
} else {
global.Picker = Picker;
}
})(this);