static

picker.js 11KB

    // picker (function (ipu, $, Hammer) { var showItemSize = 9; // 显示的子项数量, var r = 90; // 计算旋转的圆半径,结果应该缩小,是为了r不要距离容器太近 var itemAngle = 180 / showItemSize; // 每项对应的角度是 180/9 = 20 var maxExceed = itemAngle; // 滚动时允许超出边界的最大角度 // itemHeight = 40px; // 需要给出r=89是怎么计算出来的,是根据 40/2/Math.tan(40/2/180*Math.PI)=113,直接太大不好看 function Picker(slt, options) { this.el = $(slt)[0]; this.options = $.extend({}, this.defaultOptions, options); this._init(); } // 默认参数 Picker.prototype.defaultOptions = { onChange: function () { // 子项选中事件 }, listen: true, // 默认已开记监听变化 data: [] }; Picker.prototype._init = function () { var self = this; this.wrap = $(">ul", this.el); this.index = null; this.listen = !!this.options.listen; this.beginAngle = 0; this.beginExceed = this.beginAngle - maxExceed; // 最小角度值 this.stopInertiaMove = false; this.lastAngle = null; // 保存滑动前的角度 this.empty = this.options.data.length == 0; // 如果是ios,则ul的旋转中心点,有变化 if(ipu.device.ios){ this.wrap.css("transform-origin", "center center "+r+"px"); //如果是ios,要变更旋转的中心点 } this.hammer = new Hammer.Manager(this.el); this.hammer.add(new Hammer.Pan({direction: Hammer.DIRECTION_VERTICAL, threshold: 5})); this.hammer.add(new Hammer.Press({threshold: 4})); // this.hammer.on("panstart panmove panend pancancel", Hammer.bindFn(this._onPan, this)); this.hammer.on("press pressup", function (e) { // 如果用户点击了,是停止自动滚动 // console.log('press'); if(this.empty){ return ; } self.stopInertiaMove = true; if (e.type == 'pressup') { self.endScroll(); } }); this.setItems(this.options.data); }; Picker.prototype.setItems = function (data, textName) { this.wrap.empty(); // 清空历史数据 this.data = data = data || []; this.empty = data.length == 0; // 数据是否为空 this.newData = true; // 表示设置了新数据,触发change回调,但第一次设置时,不需要触发 var self = this; var lis = ""; textName = textName || 'text'; for (var i = 0, j = data.length; i < j; i++) { lis = lis + "<li>" + data[i][textName] + "</li>"; } $(lis).appendTo(this.wrap); this.items = $(">li", this.wrap); this.itemsSize = this.items.size(); this.endAngle = (this.empty ? 0 : this.itemsSize - 1) * itemAngle; this.endExceed = this.endAngle + maxExceed; // 最大旋转角度值 // 初始化各子项角度 this.items.each(function (i) { $(this).css({ "transform": "translateZ(" + r + "px) rotateX(-" + (i * itemAngle) + "deg)", "transform-origin": "center center -" + r + "px" }); $(this).click(function () { console.log('click'); self.stopInertiaMove = true; self.setAngle(i * itemAngle, true); }) }); var newAngle ; if(this.empty || this.index == null){ newAngle = 0; }else { if(this.index > this.itemsSize - 1){ newAngle = (this.itemsSize - 1) * itemAngle; }else{ newAngle = this.index * itemAngle; } } this.setAngle(newAngle, true); /*if (this.index !== null) { // 当前已经旋转则,保留当前的索引 if (this.index > this.itemsSize - 1 && !this.empty) { this.index = this.itemsSize - 1; } this.setAngle(this.index * itemAngle, true); } else { // 第一次初始数据,不触发change事件 this.index = 0; // 被调用后,不能再使用null值了 this.setAngle(0, false); // 第一次设置初始化时,不触发change事件 }*/ }; Picker.prototype._onPan = function (ev) { if(this.empty){ return ; } //console.log(ev.deltaX + "=="+ ev.deltaY); if (ev.type == 'panstart') { // 好像一定要移动才有startg事件 self.stopInertiaMove = true; this.lastAngle = this.angle; this.wrap.addClass("ui-noanimate"); // 移除动画 this.stopInertiaMove = true; // 停止自动减速滚动 // console.log('panstart'); } else if (ev.type == 'panmove') { var moveAngle = this.calcAngle(ev.deltaY); var newAngle = this.lastAngle - moveAngle; //最新的角度 //console.log('=='+newAngle); // 一个可以转动的最小值和最大值过滤 if (newAngle < this.beginExceed) { newAngle = this.beginExceed; } if (newAngle > this.endExceed) { newAngle = this.endExceed; } this.setAngle(newAngle); } else { // end or cancel事件 // console.log('end or cancel:' + ev.type); var v = ev.overallVelocityY; // 滑动的速度 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 = this.angle; var distAngle = -this.calcAngle(dist); // console.log("dist=" + dist + ", distAngle" + distAngle); //---- var srcDistAngle = distAngle; if (startAngle + distAngle < this.beginExceed) { distAngle = this.beginExceed - startAngle; duration = duration * (distAngle / srcDistAngle) * 0.6; } if (startAngle + distAngle > this.endExceed) { distAngle = this.endExceed - startAngle; duration = duration * (distAngle / srcDistAngle) * 0.6; } if (distAngle == 0) { this.endScroll(); return; } this.scrollDistAngle(startAngle, distAngle, duration); } }; // 计算移动的角度,转动的角度,就是移动的距离对应相关圆周 // 2*r*PI = 360, angle = 360*c/(2*r*PI) var ca = 360 / (2 * r * Math.PI); Picker.prototype.calcAngle = function (c) { return c * ca; }; // endScroll 是否为结束的滚动结束,滚动结束需要调用结束事件 Picker.prototype.setAngle = function (newAngle, endScroll) { this.angle = newAngle; // 存储最新值 this.wrap.css("transform", "perspective(1000px) rotateY(0deg) rotateX(" + newAngle + "deg)"); this.calcItemVisable(newAngle); if (endScroll) { var index = newAngle / itemAngle; var oldIndex = this.index; this.index = this.empty ? null : index; // 这里可以做一个判断,如果是empty,则index值可以不改变 // 这个地方要判断下,数据更新或索引更新都要触发 if (oldIndex != index || this.newData) { this.newData = false; // console.log('change'); if (this.options.onChange && this.listen) { // console.log('changed call'); this.options.onChange(this.getSelectedItem(), this.index, oldIndex, this.newData); } } } }; // 计算子项的显示情况 Picker.prototype.calcItemVisable = function (angle) { this.items.each(function (index) { var difference = Math.abs(index * itemAngle - angle); if (difference < itemAngle / 2) { $(this).addClass("ui-highlight ui-visible"); } else if (difference >= (90 - itemAngle / 2)) { // 距离不能超过90度 $(this).removeClass("ui-highlight ui-visible"); } else { $(this).addClass("ui-visible").removeClass("ui-highlight"); } }); }; // 设置最后回归位置 Picker.prototype.endScroll = function () { this.wrap.removeClass("ui-noanimate"); var endAngle; if (this.angle < this.beginAngle) { endAngle = this.beginAngle; } else if (this.angle > this.endAngle) { endAngle = this.endAngle; } else { var index = parseInt((this.angle / itemAngle).toFixed(0)); endAngle = (itemAngle * index); } this.setAngle(endAngle, true); }; // 进行惯性滚动 Picker.prototype.scrollDistAngle = function (startAngle, distAngle, duration) { var self = this; var nowTime = new Date().getTime(); this.stopInertiaMove = false; duration = 1 * duration; // 滚动时长控制修改 // hammer调用的惯性函数 (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.setListen = function (bool) { this.listen = !!bool; }; Picker.prototype.quartEaseOut = function (t, b, c, d) { return -c * ((t = t / d - 1) * t * t * t - 1) + b; }; Picker.prototype.setSelectedValue = function (value) { var self = this; for (var index in self.data) { var item = self.data[index]; if (item.value == value) { self.setAngle(index* itemAngle, true); return; } } }; // 获取当前选中的值 Picker.prototype.getSelectedItem = function () { return this.empty ? {}: this.data[this.index]; }; Picker.prototype.getSelectedValue = function () { return this.getSelectedItem().value; }; Picker.prototype.getSelectedText = function () { return this.getSelectedItem().text; }; Picker.prototype.getSelectedIndex = function () { return this.index; }; ipu.Picker = Picker; })(ipu || window, jQuery, Hammer);