custom-picker.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*
  2. 参考网址:原生js实现移动端选择器插件
  3. https://segmentfault.com/a/1190000013366588
  4. */
  5. // created by zhouatie
  6. (function () {
  7. var util = {
  8. extend: function (target) {
  9. for (var i = 1, len = arguments.length; i < len; i++) {
  10. for (var prop in arguments[i]) {
  11. if (arguments[i].hasOwnProperty(prop)) {
  12. target[prop] = arguments[i][prop];
  13. }
  14. }
  15. }
  16. return target;
  17. },
  18. indexOf: function (array, item) {
  19. var result = -1;
  20. for (var i = 0, len = array.length; i < len; i++) {
  21. if (array[i] === item) {
  22. result = i;
  23. break;
  24. }
  25. }
  26. return result;
  27. },
  28. css: function (elem, obj) {
  29. for (var i in obj) {
  30. elem.style[i] = obj[i];
  31. }
  32. },
  33. addClass: function (element, className) {
  34. var classNames = element.className.split(/\s+/);
  35. if (util.indexOf(classNames, className) == -1) {
  36. classNames.push(className);
  37. }
  38. element.className = classNames.join(' ');
  39. },
  40. removeClass: function (element, className) {
  41. var classNames = element.className.split(/\s+/);
  42. var index = util.indexOf(classNames, className);
  43. if (index !== -1) {
  44. classNames.splice(index, 1);
  45. }
  46. element.className = classNames.join(' ');
  47. },
  48. hasClass: function (element, className) {
  49. if (!element || !element.className) return false;
  50. var classNames = element.className.split(/\s+/);
  51. return util.indexOf(classNames, className) != -1;
  52. },
  53. parents: function (elem, pClass) { // 递归函数通过父亲的classname获取元素
  54. if (!elem) return null;
  55. var parent = elem.parentNode;
  56. if (parent === document) return null;
  57. if (!this.hasClass(parent, pClass)) parent = this.parents(parent, pClass);
  58. return parent;
  59. },
  60. isObj: function (o) {
  61. return Object.prototype.toString.call(o) == "[object Object]";
  62. },
  63. isArray: function (o) {
  64. return Object.prototype.toString.call(o) == "[object Array]";
  65. }
  66. };
  67. function PickerView(opt) {
  68. var _this = this;
  69. this.Opt = {
  70. title: '',
  71. leftText: '取消',
  72. rightText: '确定',
  73. saveFn: function (selectArr) {
  74. }
  75. };
  76. // 同步参数
  77. for (var i in opt) {
  78. if (opt[i]) this.Opt[i] = opt[i];
  79. }
  80. this._y_start = "";
  81. this._y_move = "";
  82. this._y_end = "";
  83. this.top_start = 0; // 移动起始点
  84. this.isMove = false; // 是否是移动聊天框
  85. this.elem_wrap = null; // 最外层的容器
  86. this.elem_leftBtn = null; // 左按钮元素
  87. this.elem_rightBtn = null; // 右按钮元素
  88. this.elem_contents = null; // items容器
  89. this.elem_mask = null; // 黑色背景
  90. var selectcache = this.Opt.bindElem.getAttribute("selectcache");
  91. this.selectcache = selectcache ? selectcache.split(",") : [];
  92. this.selectArr = []; // 选项对应的元素序列号 如:[0,0,0]
  93. this.init();
  94. }
  95. PickerView.VERSION = '1.0.0';
  96. PickerView.defaultOpt = {
  97. headerHeight: 45, // 头部默认高度
  98. itemHeight: 34, // 每个item的默认高度
  99. };
  100. PickerView.prototype = {
  101. constructor: PickerView,
  102. getItemTpl: function (keys) {
  103. var item_html = "";
  104. for (var i = 0; i < keys.length; i++) {
  105. var str = JSON.stringify(keys[i]);
  106. item_html += '<div class="pickerView-item" data-item=\''+ str +'\'>' + keys[i].name + '</div>';
  107. };
  108. return item_html;
  109. },
  110. getItemsTpl: function (keys) {
  111. var fieldIndex = this.selectcache[this.selectArr.length] ? this.selectcache[this.selectArr.length] : 0;
  112. this.selectArr.push(fieldIndex);
  113. var html = "",
  114. len = -fieldIndex * PickerView.defaultOpt.itemHeight,
  115. item_html = this.getItemTpl(keys);
  116. html += '<div index="' + (this.selectArr.length - 1) + '" class="pickerView-box-content">' +
  117. '<div style="background-size:100% ' + this.padding + 'px;" class="pickerView-box-content-mask"></div>' +
  118. '<div style="top:' + this.padding + 'px;" class="pickerView-box-content-indicator"></div>' +
  119. '<div style="padding:' + this.padding + 'px 0;transform:translate3d(0,' + len + 'px,0)" fieldIndex="0" class="pickerView-items">' +
  120. item_html +
  121. '</div>' +
  122. '</div>';
  123. return html;
  124. },
  125. renderItems: function (obj) {
  126. var _this = this,
  127. html = "",
  128. arr = obj,
  129. isObj = util.isObj(obj);
  130. if (isObj) arr = Object.keys(obj);
  131. html += this.getItemsTpl(arr);
  132. var fieldIndex = this.selectArr[this.selectArr.length - 1];
  133. if (isObj) html += this.renderItems(obj[arr[fieldIndex]]);
  134. return html;
  135. },
  136. getTpl: function () {
  137. var html = '<div class="pickerView-mask"></div><div class="pickerView-box">' +
  138. '<div class="pickerView-box-header">' +
  139. '<div class="pickerView-box-header-left pickerView-box-header-btn">取消</div>' +
  140. '<div class="pickerView-box-header-title">' + this.Opt.title + '</div>' +
  141. '<div class="pickerView-box-header-right pickerView-box-header-btn">确定</div>' +
  142. '</div>' +
  143. '<div class="pickerView-box-content-wrap">';
  144. html += this.renderItems(this.Opt.data);
  145. html += '</div></div>';
  146. return html;
  147. },
  148. init: function () {
  149. var _this = this,
  150. body = document.getElementsByTagName("body")[0],
  151. div = document.createElement("div");
  152. div.className = "pickerView-wrap";
  153. this.elem_wrap = div;
  154. this.padding = (document.documentElement.clientHeight * 0.4 - PickerView.defaultOpt.headerHeight - PickerView.defaultOpt.itemHeight) / 2;
  155. div.innerHTML = this.getTpl();
  156. body.appendChild(div);
  157. this.elem_mask = this.elem_wrap.getElementsByClassName("pickerView-mask")[0];
  158. this.elem_contents = this.elem_wrap.getElementsByClassName("pickerView-box-content-wrap")[0];
  159. this.elem_leftBtn = this.elem_wrap.getElementsByClassName("pickerView-box-header-left")[0];
  160. this.elem_rightBtn = this.elem_wrap.getElementsByClassName("pickerView-box-header-right")[0];
  161. this.elem_contents.addEventListener("touchstart", function (e) {
  162. _this.moveObj = util.parents(e.target, "pickerView-box-content").children[2];
  163. _this.touchstart(e);
  164. e.stopPropagation();
  165. }, false);
  166. this.elem_contents.addEventListener("touchmove", function (e) {
  167. _this.touchmove(e);
  168. e.stopPropagation();
  169. e.preventDefault();
  170. }, false);
  171. this.elem_contents.addEventListener("touchend", function (e) {
  172. _this.touchend(e);
  173. e.stopPropagation();
  174. }, false);
  175. this.elem_mask.addEventListener("touchend", function (e) {
  176. _this.closeComponent();
  177. e.stopPropagation();
  178. e.preventDefault()
  179. }, false);
  180. this.elem_leftBtn.addEventListener("touchend", function (e) {
  181. _this.closeComponent();
  182. e.stopPropagation();
  183. e.preventDefault()
  184. }, false);
  185. this.elem_rightBtn.addEventListener("touchend", function (e) {
  186. var selectArr = [];
  187. for (var i = 0; i < _this.elem_contents.children.length; i++) {
  188. var items = _this.elem_contents.children[i].children[2],
  189. // field = items.children.length > 0 ? items.children[_this.selectArr[i]].innerText : "";
  190. field = items.children.length > 0 ? items.children[_this.selectArr[i]].getAttribute("data-item") : "";
  191. selectArr.push(JSON.parse(field));
  192. }
  193. _this.Opt.rightFn(selectArr);
  194. _this.closeComponent();
  195. // 绑定元素
  196. _this.Opt.bindElem.setAttribute("selectcache", _this.selectArr);
  197. e.stopPropagation();
  198. e.preventDefault()
  199. }, false);
  200. },
  201. touchstart: function (e) {
  202. this._y_start = e.touches[0].pageY;
  203. this.isMove = false;
  204. this.top_start = parseInt(this.moveObj.style.transform.split(",")[1]);
  205. },
  206. touchmove: function (e) {
  207. var _this = this;
  208. this.isMove = true;
  209. this._y_move = e.touches[0].pageY;
  210. var len = parseFloat(this._y_move) - parseFloat(this._y_start) + parseFloat(this.top_start);
  211. util.css(_this.moveObj, {
  212. "transform": 'translate3d(0,' + len + 'px,0)'
  213. })
  214. this.top_end = len;
  215. },
  216. touchend: function (e) {
  217. if (!this.isMove) return;
  218. this.isMove = false;
  219. var _this = this,
  220. itemHeight = PickerView.defaultOpt.itemHeight,
  221. sign = this.top_end >= 0 ? 1 : -1,
  222. index = this.moveObj.parentNode.getAttribute("index"),
  223. fieldIndex = Math.round(Math.abs(this.top_end) / itemHeight),
  224. len = sign * (fieldIndex * itemHeight);
  225. if (len > 0) {
  226. len = 0;
  227. fieldIndex = 0;
  228. } else if (len < -(this.moveObj.children.length - 1) * itemHeight) {
  229. len = -(this.moveObj.children.length - 1) * itemHeight;
  230. fieldIndex = this.moveObj.children.length - 1;
  231. };
  232. this.selectArr[index] = fieldIndex;
  233. this.moveObj.setAttribute("fieldIndex", fieldIndex);
  234. this.moveObj.style.transition = "0.3s cubic-bezier(0,0,0.2,1.15)";
  235. util.css(_this.moveObj, {
  236. "transform": 'translate3d(0,' + len + 'px,0)'
  237. });
  238. _this.changeNext(index);
  239. _this.moveObj.addEventListener("transitionend", function (event) {
  240. _this.moveObj.style.transition = "";
  241. }, false);
  242. _this.moveObj.addEventListener("webkitTransitionEnd", function (event) {
  243. _this.moveObj.style.transition = "";
  244. }, false);
  245. },
  246. changeNext: function (index) {
  247. var data = this.Opt.data,
  248. arr = [];
  249. for (var i = 0; i < this.selectArr.length; i++) {
  250. var elem_items = this.elem_contents.children[i].children[2];
  251. if (i > index) {
  252. util.css(elem_items, {
  253. "transform": 'translate3d(0,0,0)'
  254. });
  255. this.selectArr[i] = 0;
  256. arr = util.isObj(data) ? Object.keys(data) : data;
  257. elem_items.innerHTML = this.getItemTpl(arr);
  258. var field = arr[0];
  259. data = data[field];
  260. } else {
  261. var field = elem_items.children[this.selectArr[i]].innerText;
  262. data = data[field];
  263. }
  264. }
  265. },
  266. closeComponent: function () {
  267. var body = document.getElementsByTagName("body")[0];
  268. body.removeChild(this.elem_wrap);
  269. }
  270. }
  271. window.PickerView = PickerView;
  272. })();