|
// 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);
|