/** * 选择列表插件 * varstion 2.0.0 * by Houfeng * Houfeng@DCloud.io */ (function($, window, document, undefined) { var MAX_EXCEED = 30; var VISIBLE_RANGE = 90; var DEFAULT_ITEM_HEIGHT = 40; var BLUR_WIDTH = 10; var rad2deg = $.rad2deg = function(rad) { return rad / (Math.PI / 180); }; var deg2rad = $.deg2rad = function(deg) { return deg * (Math.PI / 180); }; var platform = navigator.platform.toLowerCase(); var userAgent = navigator.userAgent.toLowerCase(); var isIos = (userAgent.indexOf('iphone') > -1 || userAgent.indexOf('ipad') > -1 || userAgent.indexOf('ipod') > -1) && (platform.indexOf('iphone') > -1 || platform.indexOf('ipad') > -1 || platform.indexOf('ipod') > -1); //alert(isIos); var Picker = $.Picker = function(holder, options) { var self = this; self.holder = holder; self.options = options || {}; self.init(); self.initInertiaParams(); self.calcElementItemPostion(true); self.bindEvent(); }; Picker.prototype.findElementItems = function() { var self = this; self.elementItems = [].slice.call(self.holder.querySelectorAll('li')); return self.elementItems; }; Picker.prototype.init = function() { var self = this; self.list = self.holder.querySelector('ul'); self.findElementItems(); self.height = self.holder.offsetHeight; self.r = self.height / 2 - BLUR_WIDTH; self.d = self.r * 2; self.itemHeight = self.elementItems.length > 0 ? self.elementItems[0].offsetHeight : DEFAULT_ITEM_HEIGHT; self.itemAngle = parseInt(self.calcAngle(self.itemHeight * 0.8)); self.hightlightRange = self.itemAngle / 2; self.visibleRange = VISIBLE_RANGE; self.beginAngle = 0; self.beginExceed = self.beginAngle - MAX_EXCEED; self.list.angle = self.beginAngle; if (isIos) { self.list.style.webkitTransformOrigin = "center center " + self.r + "px"; } }; Picker.prototype.calcElementItemPostion = function(andGenerateItms) { var self = this; if (andGenerateItms) { self.items = []; } self.elementItems.forEach(function(item) { var index = self.elementItems.indexOf(item); self.endAngle = self.itemAngle * index; item.angle = self.endAngle; item.style.webkitTransformOrigin = "center center -" + self.r + "px"; item.style.webkitTransform = "translateZ(" + self.r + "px) rotateX(" + (-self.endAngle) + "deg)"; if (andGenerateItms) { var dataItem = {}; dataItem.text = item.innerHTML || ''; dataItem.value = item.getAttribute('data-value') || dataItem.text; self.items.push(dataItem); } }); self.endExceed = self.endAngle + MAX_EXCEED; self.calcElementItemVisibility(self.beginAngle); }; Picker.prototype.calcAngle = function(c) { var self = this; var a = b = parseFloat(self.r); //直径的整倍数部分直接乘以 180 c = Math.abs(c); //只算角度不关心正否值 var intDeg = parseInt(c / self.d) * 180; c = c % self.d; //余弦 var cosC = (a * a + b * b - c * c) / (2 * a * b); var angleC = intDeg + rad2deg(Math.acos(cosC)); return angleC; }; Picker.prototype.calcElementItemVisibility = function(angle) { var self = this; self.elementItems.forEach(function(item) { var difference = Math.abs(item.angle - angle); if (difference < self.hightlightRange) { item.classList.add('highlight'); } else if (difference < self.visibleRange) { item.classList.add('visible'); item.classList.remove('highlight'); } else { item.classList.remove('highlight'); item.classList.remove('visible'); } }); }; Picker.prototype.setAngle = function(angle) { var self = this; self.list.angle = angle; self.list.style.webkitTransform = "perspective(1000px) rotateY(0deg) rotateX(" + angle + "deg)"; self.calcElementItemVisibility(angle); }; Picker.prototype.bindEvent = function() { var self = this; var lastAngle = 0; var startY = null; var isPicking = false; self.holder.addEventListener($.EVENT_START, function(event) { isPicking = true; event.preventDefault(); self.list.style.webkitTransition = ''; startY = (event.changedTouches ? event.changedTouches[0] : event).pageY; lastAngle = self.list.angle; self.updateInertiaParams(event, true); }, false); self.holder.addEventListener($.EVENT_END, function(event) { isPicking = false; event.preventDefault(); self.startInertiaScroll(event); }, false); self.holder.addEventListener($.EVENT_CANCEL, function(event) { isPicking = false; event.preventDefault(); self.startInertiaScroll(event); }, false); self.holder.addEventListener($.EVENT_MOVE, function(event) { if (!isPicking) { return; } event.preventDefault(); var endY = (event.changedTouches ? event.changedTouches[0] : event).pageY; var dragRange = endY - startY; var dragAngle = self.calcAngle(dragRange); var newAngle = dragRange > 0 ? lastAngle - dragAngle : lastAngle + dragAngle; if (newAngle > self.endExceed) { newAngle = self.endExceed } if (newAngle < self.beginExceed) { newAngle = self.beginExceed } self.setAngle(newAngle); self.updateInertiaParams(event); }, false); //-- self.list.addEventListener('tap', function(event) { elementItem = event.target; if (elementItem.tagName == 'LI') { self.setSelectedIndex(self.elementItems.indexOf(elementItem), 200); } }, false); }; Picker.prototype.initInertiaParams = function() { var self = this; self.lastMoveTime = 0; self.lastMoveStart = 0; self.stopInertiaMove = false; }; Picker.prototype.updateInertiaParams = function(event, isStart) { var self = this; var point = event.changedTouches ? event.changedTouches[0] : event; if (isStart) { self.lastMoveStart = point.pageY; self.lastMoveTime = event.timeStamp || Date.now(); self.startAngle = self.list.angle; } else { var nowTime = event.timeStamp || Date.now(); if (nowTime - self.lastMoveTime > 300) { self.lastMoveTime = nowTime; self.lastMoveStart = point.pageY; } } self.stopInertiaMove = true; }; Picker.prototype.startInertiaScroll = function(event) { var self = this; var point = event.changedTouches ? event.changedTouches[0] : event; /** * 缓动代码 */ var nowTime = event.timeStamp || Date.now(); var v = (point.pageY - self.lastMoveStart) / (nowTime - self.lastMoveTime); //最后一段时间手指划动速度 var dir = v > 0 ? -1 : 1; //加速度方向 var deceleration = dir * 0.0006 * -1; var duration = Math.abs(v / deceleration); // 速度消减至0所需时间 var dist = v * duration / 2; //最终移动多少 var startAngle = self.list.angle; var distAngle = self.calcAngle(dist) * dir; //---- var srcDistAngle = distAngle; if (startAngle + distAngle < self.beginExceed) { distAngle = self.beginExceed - startAngle; duration = duration * (distAngle / srcDistAngle) * 0.6; } if (startAngle + distAngle > self.endExceed) { distAngle = self.endExceed - startAngle; duration = duration * (distAngle / srcDistAngle) * 0.6; } //---- if (distAngle == 0) { self.endScroll(); return; } self.scrollDistAngle(nowTime, startAngle, distAngle, duration); }; Picker.prototype.scrollDistAngle = function(nowTime, startAngle, distAngle, duration) { var self = this; self.stopInertiaMove = false; (function(nowTime, startAngle, distAngle, duration) { var frameInterval = 13; var stepCount = duration / frameInterval; var stepIndex = 0; (function inertiaMove() { if (self.stopInertiaMove) return; var newAngle = self.quartEaseOut(stepIndex, startAngle, distAngle, stepCount); self.setAngle(newAngle); stepIndex++; if (stepIndex > stepCount - 1 || newAngle < self.beginExceed || newAngle > self.endExceed) { self.endScroll(); return; } setTimeout(inertiaMove, frameInterval); })(); })(nowTime, startAngle, distAngle, duration); }; Picker.prototype.quartEaseOut = function(t, b, c, d) { return -c * ((t = t / d - 1) * t * t * t - 1) + b; }; Picker.prototype.endScroll = function() { var self = this; if (self.list.angle < self.beginAngle) { self.list.style.webkitTransition = "150ms ease-out"; self.setAngle(self.beginAngle); } else if (self.list.angle > self.endAngle) { self.list.style.webkitTransition = "150ms ease-out"; self.setAngle(self.endAngle); } else { var index = parseInt((self.list.angle / self.itemAngle).toFixed(0)); self.list.style.webkitTransition = "100ms ease-out"; self.setAngle(self.itemAngle * index); } self.triggerChange(); }; Picker.prototype.triggerChange = function(force) { var self = this; setTimeout(function() { var index = self.getSelectedIndex(); var item = self.items[index]; if ($.trigger && (index != self.lastIndex || force === true)) { $.trigger(self.holder, 'change', { "index": index, "item": item }); //console.log('change:' + index); } self.lastIndex = index; typeof force === 'function' && force(); }, 0); }; Picker.prototype.correctAngle = function(angle) { var self = this; if (angle < self.beginAngle) { return self.beginAngle; } else if (angle > self.endAngle) { return self.endAngle; } else { return angle; } }; Picker.prototype.setItems = function(items) { var self = this; self.items = items || []; var buffer = []; self.items.forEach(function(item) { if (item !== null && item !== undefined) { buffer.push('