mui.listpicker.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /**
  2. * 选择列表插件
  3. * varstion 1.0.1
  4. * by Houfeng
  5. * Houfeng@DCloud.io
  6. */
  7. (function($, document) {
  8. //创建 DOM
  9. $.dom = function(str) {
  10. if (typeof(str) !== 'string') {
  11. if ((str instanceof Array) || (str[0] && str.length)) {
  12. return [].slice.call(str);
  13. } else {
  14. return [str];
  15. }
  16. }
  17. if (!$.__create_dom_div__) {
  18. $.__create_dom_div__ = document.createElement('div');
  19. }
  20. $.__create_dom_div__.innerHTML = str;
  21. return [].slice.call($.__create_dom_div__.childNodes);
  22. };
  23. var _listpickerId = 0;
  24. var ListPicker = $.ListPicker = $.Class.extend({
  25. init: function(box, options) {
  26. var self = this;
  27. if (!box) {
  28. throw "构造 ListPicker 时找不到元素";
  29. }
  30. self.box = box;
  31. //避免重复初始化开始
  32. if (self.box.listpickerId) return;
  33. self.listpickerId = self.box.listpickerId = "listpicker-" + (++_listpickerId);
  34. //避免重复初始化结束
  35. self.box.setAttribute('data-listpicker-id', self.box.listpickerId);
  36. //处理 options
  37. options = options || {};
  38. options.fiexdDur = options.fiexdDur || 150;
  39. options.highlightStyle = options.highlightStyle || 'color: green;';
  40. //在 ios 上启用 h5 模式,
  41. if ($.os.ios) {
  42. options.enabledH5 = true;
  43. }
  44. //如果没有设定 enabled3d,将默认用 3d 模式
  45. if (options.enabled3d === null || typeof options.enabled3d === 'undefined') {
  46. options.enabled3d = $.os.ios;
  47. }
  48. //
  49. self.options = options;
  50. self._create();
  51. self._handleShim();
  52. self._bindEvent();
  53. self._applyToBox();
  54. self._handleHighlight();
  55. },
  56. _create: function() {
  57. var self = this;
  58. self.boxInner = $('.mui-listpicker-inner', self.box)[0];
  59. self.boxHeight = self.box.offsetHeight;
  60. self.list = $('ul', self.boxInner)[0];
  61. //refresh 中会执行 self.itemElementArray = [].slice.call($('li', self.list));
  62. self.refresh();
  63. var firstItem = self.itemElementArray[0];
  64. self.itemHeight = 0;
  65. if (firstItem) {
  66. self.itemHeight = firstItem.offsetHeight;
  67. } else {
  68. self.list.innerHTML = "<li>...</li>";
  69. firstItem = $('li', self.list)[0];
  70. self.itemHeight = firstItem.offsetHeight;
  71. self.list.innerHTML = '';
  72. }
  73. self.list.style.paddingTop = self.list.style.paddingBottom = (self.boxHeight / 2 - self.itemHeight / 2) + 'px';
  74. //创建中间选中项的高亮行
  75. self.rule = $.dom('<div class="mui-listpicker-rule"> </div>')[0];
  76. self.rule.style.height = self.itemHeight + 'px';
  77. self.rule.style.marginTop = -(self.itemHeight / 2) + 'px';
  78. self.box.appendChild(self.rule);
  79. self.middle = self.boxInner.offsetHeight / 2;
  80. self.showLine = parseInt((self.boxInner.offsetHeight / self.itemHeight).toFixed(0));
  81. //是否启用 3d 效果
  82. if (self.options.enabled3d) {
  83. self.box.classList.add('three-dimensional');
  84. }
  85. },
  86. //根据 options 处理不同平台兼容问题
  87. _handleShim: function() {
  88. var self = this;
  89. if (self.options.enabledH5) {
  90. self.options.fiexdDur *= 2;
  91. self.boxInner.classList.add($.className('scroll-wrapper'));
  92. self.list.classList.add($.className('scroll'));
  93. self._scrollerApi = $(self.boxInner).scroll({
  94. deceleration: 0.002
  95. });
  96. //增加惯性滚动时的 scroll 触发处理
  97. //shimTetTranslate(self._scrollerApi);
  98. //--
  99. self.setScrollTop = function(y, dur, callback) {
  100. self._scrollerApi.scrollTo(0, -y, dur);
  101. };
  102. self.getScrollTop = function() {
  103. var self = this;
  104. if (self._scrollerApi.lastY > 0) {
  105. return 0
  106. } else {
  107. return Math.abs(self._scrollerApi.lastY);
  108. }
  109. };
  110. } else {
  111. //alert(0);
  112. //为 boxInner 增加 scrollend 事件 (没有原生 scrollend 事件)
  113. self.boxInner.addEventListener('scroll', function(event) {
  114. if (self.disabledScroll) return;
  115. self.isScrolling = true;
  116. if (self.scrollTimer) {
  117. clearTimeout(self.scrollTimer);
  118. }
  119. self.scrollTimer = setTimeout(function() {
  120. self.isScrolling = false;
  121. if (!self.isTouchDown || !$.os.ios) {
  122. $.trigger(self.boxInner, 'scrollend');
  123. }
  124. }, 150);
  125. }, false);
  126. self.aniScrollTop = function(y, dur, callback) {
  127. self.disabledScroll = true;
  128. var stepNum = dur > 0 ? dur / 10 : 1;
  129. var stepSize = (y - self.boxInner.scrollTop) / stepNum;
  130. self._lastScrollTop = self.boxInner.scrollTop; //记录最后的位置
  131. self._aniScrollTop(y, 0, stepNum, stepSize, callback);
  132. };
  133. self._aniScrollTop = function(y, stepIndex, stepNum, stepSize, callback) {
  134. self.boxInner.scrollTop = self._lastScrollTop + stepSize * stepIndex;
  135. if (stepIndex < stepNum) {
  136. setTimeout(function() {
  137. self._aniScrollTop(y, ++stepIndex, stepNum, stepSize);
  138. }, 10);
  139. } else {
  140. //self.boxInner.scrollTop = y;
  141. self.disabledScroll = false;
  142. if (callback) callback();
  143. }
  144. };
  145. self.setScrollTop = function(y, dur, callback) {
  146. self.aniScrollTop(y, dur);
  147. };
  148. self.getScrollTop = function() {
  149. var self = this;
  150. return self.boxInner.scrollTop;
  151. };
  152. //在 ios 上手指不弹起时,防止定位抖动开始
  153. if ($.os.ios) {
  154. self.boxInner.addEventListener('touchstart', function(event) {
  155. var self = this;
  156. self.isTouchDown = true;
  157. }, false);
  158. self.boxInner.addEventListener('touchend', function(event) {
  159. self.isTouchDown = false;
  160. if (!self.isScrolling) {
  161. setTimeout(function() {
  162. $.trigger(self.boxInner, 'scrollend');
  163. }, 0);
  164. }
  165. }, false);
  166. }
  167. //在 ios 上手指不弹起时,防止定位抖动结束
  168. }
  169. },
  170. _handleHighlight: function() {
  171. var self = this;
  172. var scrollTop = self.getScrollTop();
  173. var fiexd = parseInt((scrollTop / self.itemHeight).toFixed(0));
  174. var lastIndex = self.itemElementArray.length - 1;
  175. var displayRange = parseInt((self.showLine / 2).toFixed(0));
  176. var displayBegin = fiexd - displayRange;
  177. var displayEnd = fiexd + displayRange;
  178. if (displayBegin < 0) {
  179. displayBegin = 0;
  180. }
  181. if (displayEnd > lastIndex) {
  182. displayEnd = lastIndex;
  183. }
  184. //高亮选中行开始
  185. for (var index = displayBegin; index <= displayEnd; index++) {
  186. var itemElement = self.itemElementArray[index];
  187. if (index == fiexd) {
  188. itemElement.classList.add($.className('listpicker-item-selected'));
  189. //itemElement.classList.remove($.className('listpicker-item-before'));
  190. //itemElement.classList.remove($.className('listpicker-item-after'));
  191. } else {
  192. //itemElement.classList.add($.className('listpicker-item-' + (fiexd > index ? 'before' : 'after')));
  193. itemElement.classList.remove($.className('listpicker-item-selected'));
  194. }
  195. if (self.options.enabled3d) {
  196. //3d 处理开始
  197. var itemOffset = self.middle - (itemElement.offsetTop - scrollTop + self.itemHeight / 2) + 1;
  198. var percentage = itemOffset / self.itemHeight;
  199. var angle = (18 * percentage);
  200. //if (angle > 180) angle = 180;
  201. //if (angle < -180) angle = -180;
  202. itemElement.style.webkitTransform = 'rotateX(' + angle + 'deg) translate3d(0px,0px,' + (0 - Math.abs(percentage * 12)) + 'px)';
  203. //3d 处理结束
  204. }
  205. }
  206. },
  207. _triggerChange: function() {
  208. var self = this;
  209. $.trigger(self.box, 'change', {
  210. index: self.getSelectedIndex(),
  211. value: self.getSelectedValue(),
  212. text: self.getSelectedText(),
  213. item: self.getSelectedItem(),
  214. element: self.getSelectedElement()
  215. });
  216. },
  217. _scrollEndHandle: function() {
  218. var self = this;
  219. var scrollTop = self.getScrollTop();
  220. var fiexd = (scrollTop / self.itemHeight).toFixed(0);
  221. self.disabledScrollEnd = true;
  222. self.setSelectedIndex(fiexd);
  223. self._triggerChange();
  224. self._handleHighlight();
  225. setTimeout(function() {
  226. self.disabledScrollEnd = false;
  227. self._handleHighlight();
  228. }, self.options.fiexdDur);
  229. },
  230. _bindEvent: function() {
  231. var self = this;
  232. //滚动处理高亮
  233. self.boxInner.addEventListener('scroll', function(event) {
  234. self._handleHighlight(event);
  235. }, false);
  236. //处理滚动结束
  237. self.disabledScrollEnd = false;
  238. self.boxInner.addEventListener('scrollend', function(event) {
  239. if (self.disabledScrollEnd) return;
  240. self.disabledScrollEnd = true;
  241. self._scrollEndHandle();
  242. }, false);
  243. //绑定项 tap 事件
  244. $(self.boxInner).on('tap', 'li', function(event) {
  245. var tapItem = this;
  246. var items = [].slice.call($('li', self.list));
  247. for (var i in items) {
  248. var item = items[i];
  249. if (item == tapItem) {
  250. self.setSelectedIndex(i);
  251. return;
  252. }
  253. };
  254. });
  255. },
  256. getSelectedIndex: function() {
  257. var self = this;
  258. return (self.getScrollTop() / self.itemHeight).toFixed(0);
  259. },
  260. setSelectedIndex: function(index, noAni) {
  261. var self = this;
  262. index = (index || 0);
  263. self.setScrollTop(self.itemHeight * index, noAni ? 0 : self.options.fiexdDur);
  264. },
  265. getSelectedElement: function() {
  266. var self = this;
  267. var index = self.getSelectedIndex();
  268. return $('li', self.list)[index];
  269. },
  270. getSelectedItem: function() {
  271. var self = this;
  272. var itemElement = self.getSelectedElement();
  273. if (!itemElement) return null;
  274. var itemJson = itemElement.getAttribute('data-item');
  275. return itemJson ? JSON.parse(itemJson) : {
  276. text: itemElement.innerText,
  277. value: itemElement.getAttribute('data-value')
  278. };
  279. },
  280. refresh: function() {
  281. var self = this;
  282. self.itemElementArray = [].slice.call($('li', self.list));
  283. },
  284. setItems: function(items) {
  285. var self = this;
  286. var buffer = [];
  287. for (index in items) {
  288. var item = items[index] || {
  289. text: 'null',
  290. value: 'null' + index
  291. };
  292. var itemJson = JSON.stringify(item);
  293. buffer.push("<li data-value='" + item.value + "' data-item='" + itemJson + "'>" + item.text + "</li>");
  294. };
  295. self.list.innerHTML = buffer.join('');
  296. if (self._scrollerApi && self._scrollerApi.refresh) {
  297. self._scrollerApi.refresh();
  298. }
  299. self.refresh();
  300. self._handleHighlight();
  301. self._triggerChange();
  302. },
  303. getItems: function() {
  304. var self = this;
  305. var items = [];
  306. var itemElements = $('li', self.list);
  307. for (index in itemElements) {
  308. var itemElement = itemElements[index];
  309. var itemJson = itemElement.getAttribute('data-item');
  310. items.push(itemJson ? JSON.parse(itemJson) : {
  311. text: itemElement.innerText,
  312. value: itemElement.getAttribute('data-value')
  313. });
  314. }
  315. return items;
  316. },
  317. getSelectedValue: function() {
  318. var self = this;
  319. var item = self.getSelectedItem();
  320. if (!item) return null;
  321. return item.value;
  322. },
  323. getSelectedText: function() {
  324. var self = this;
  325. var item = self.getSelectedItem();
  326. if (!item) return null;
  327. return item.text;
  328. },
  329. setSelectedValue: function(value, noAni) {
  330. var self = this;
  331. var itemElements = $('li', self.list);
  332. for (index in itemElements) {
  333. var itemElement = itemElements[index];
  334. if (!itemElement || !itemElement.getAttribute) {
  335. continue;
  336. }
  337. if (itemElement.getAttribute('data-value') == value) {
  338. self.setSelectedIndex(index, noAni);
  339. return;
  340. }
  341. }
  342. },
  343. _applyToBox: function() {
  344. var self = this;
  345. var memberArray = [
  346. "getSelectedIndex",
  347. "setSelectedIndex",
  348. "getSelectedElement",
  349. "getSelectedItem",
  350. "setItems",
  351. "getItems",
  352. "getSelectedValue",
  353. "getSelectedText",
  354. "setSelectedValue"
  355. ];
  356. var _clone = function(name) {
  357. if (typeof self[name] === 'function') {
  358. self.box[name] = function() {
  359. return self[name].apply(self, arguments);
  360. };
  361. } else {
  362. self.box[name] = self[name];
  363. }
  364. };
  365. for (var i in memberArray) {
  366. var name = memberArray[i];
  367. _clone(name);
  368. }
  369. }
  370. });
  371. //添加 locker 插件
  372. $.fn.listpicker = function(options) {
  373. //遍历选择的元素
  374. this.each(function(i, element) {
  375. if (options) {
  376. new ListPicker(element, options);
  377. } else {
  378. var optionsText = element.getAttribute('data-listpicker-options');
  379. var _options = optionsText ? JSON.parse(optionsText) : {};
  380. _options.enabledH5 = element.getAttribute('data-listpicker-enabledh5') || _options.enabledH5;
  381. _options.enabled3d = element.getAttribute('data-listpicker-enabled3d') || _options.enabled3d;
  382. _options.fixedDur = element.getAttribute('data-listpicker-fixddur') || _options.fixedDur;
  383. new ListPicker(element, _options);
  384. }
  385. });
  386. return this;
  387. };
  388. //自动初始化
  389. $.ready(function() {
  390. $('.mui-listpicker').listpicker();
  391. });
  392. })(mui, document);