mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-07 15:07:13 +08:00
Side menu controller
This commit is contained in:
180
hacking/SideMenuController.js
Normal file
180
hacking/SideMenuController.js
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
(function(window, document, ionic) {
|
||||||
|
|
||||||
|
SideMenuController = function(options) {
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
|
||||||
|
this.left = options.left;
|
||||||
|
this.right = options.right;
|
||||||
|
this.content = options.content;
|
||||||
|
|
||||||
|
this._rightShowing = false;
|
||||||
|
this._leftShowing = false;
|
||||||
|
|
||||||
|
this.content.onDrag = function(e) {
|
||||||
|
_this._handleDrag(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.content.endDrag = function(e) {
|
||||||
|
_this._endDrag(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
SideMenuController.prototype = {
|
||||||
|
toggleLeft: function() {
|
||||||
|
var openAmount = this.getOpenAmount();
|
||||||
|
if(openAmount > 0) {
|
||||||
|
this.openPercentage(0);
|
||||||
|
} else {
|
||||||
|
this.openPercentage(100);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toggleRight: function() {
|
||||||
|
var openAmount = this.getOpenAmount();
|
||||||
|
if(openAmount < 0) {
|
||||||
|
this.openPercentage(0);
|
||||||
|
} else {
|
||||||
|
this.openPercentage(-100);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getOpenAmount: function() {
|
||||||
|
return this.content.getTranslateX() || 0;
|
||||||
|
},
|
||||||
|
getOpenRatio: function() {
|
||||||
|
var amount = this.getOpenAmount();
|
||||||
|
if(amount >= 0) {
|
||||||
|
return amount / this.left.width;
|
||||||
|
}
|
||||||
|
return amount / this.right.width;
|
||||||
|
},
|
||||||
|
getOpenPercentage: function() {
|
||||||
|
return this.getOpenRatio() * 100;
|
||||||
|
},
|
||||||
|
openPercentage: function(percentage) {
|
||||||
|
var p = percentage / 100;
|
||||||
|
var maxLeft = this.left.width;
|
||||||
|
var maxRight = this.right.width;
|
||||||
|
if(percentage >= 0) {
|
||||||
|
this.openAmount(maxLeft * p);
|
||||||
|
} else {
|
||||||
|
this.openAmount(maxRight * p);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openAmount: function(amount) {
|
||||||
|
var maxLeft = this.left.width;
|
||||||
|
var maxRight = this.right.width;
|
||||||
|
|
||||||
|
// Check if we can move to that side, depending if the left/right panel is enabled
|
||||||
|
if((!this.left.isEnabled && amount > 0) || (!this.right.isEnabled && amount < 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((this._leftShowing && amount > maxLeft) || (this._rightShowing && amount < -maxRight)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.content.setTranslateX(amount);
|
||||||
|
|
||||||
|
if(amount >= 0) {
|
||||||
|
this._leftShowing = true;
|
||||||
|
this._rightShowing = false;
|
||||||
|
|
||||||
|
// Push the z-index of the right menu down
|
||||||
|
this.right.pushDown();
|
||||||
|
// Bring the z-index of the left menu up
|
||||||
|
this.left.bringUp();
|
||||||
|
} else {
|
||||||
|
this._rightShowing = true;
|
||||||
|
this._leftShowing = false;
|
||||||
|
|
||||||
|
// Bring the z-index of the right menu up
|
||||||
|
this.right.bringUp();
|
||||||
|
// Push the z-index of the left menu down
|
||||||
|
this.left.pushDown();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
snapToRest: function(e) {
|
||||||
|
// We want to animate at the end of this
|
||||||
|
this.content.enableAnimation();
|
||||||
|
this._isDragging = false;
|
||||||
|
|
||||||
|
// Check how much the panel is open after the drag, and
|
||||||
|
// what the drag velocity is
|
||||||
|
var ratio = this.getOpenRatio();
|
||||||
|
|
||||||
|
|
||||||
|
if(ratio == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var velocityThreshold = 0.3;
|
||||||
|
var velocityX = e.gesture.velocityX
|
||||||
|
var direction = e.gesture.direction;
|
||||||
|
|
||||||
|
// Less than half, going left
|
||||||
|
//if(ratio > 0 && ratio < 0.5 && direction == 'left' && velocityX < velocityThreshold) {
|
||||||
|
//this.openPercentage(0);
|
||||||
|
//}
|
||||||
|
|
||||||
|
var sign = ratio && ratio / Math.abs(ratio);
|
||||||
|
|
||||||
|
// Left panel, More than positive half, too slow
|
||||||
|
if((ratio > 0.5 || ratio < -0.5) && velocityX < velocityThreshold) {
|
||||||
|
this.openPercentage(sign * 100);
|
||||||
|
}
|
||||||
|
// Left or Right Panel, Less than +/- half, too slow
|
||||||
|
else if(ratio <= 0.5 && ratio >= -0.5 && velocityX < velocityThreshold) {
|
||||||
|
this.openPercentage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left panel, Going left, quickly
|
||||||
|
else if(direction == 'left' && ratio >= 0 && velocityX >= velocityThreshold) {
|
||||||
|
this.openPercentage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left panel, Going right, quickly
|
||||||
|
else if(direction == 'right' && ratio >= 0 && velocityX >= velocityThreshold) {
|
||||||
|
this.openPercentage(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right panel, Going left, quickly
|
||||||
|
else if(direction == 'left' && ratio <= 0 && velocityX >= velocityThreshold) {
|
||||||
|
this.openPercentage(-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right panel, Going right, quickly
|
||||||
|
else if(direction == 'right' && ratio <= 0 && velocityX >= velocityThreshold) {
|
||||||
|
this.openPercentage(0);
|
||||||
|
} else {
|
||||||
|
this.openPercentage(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_endDrag: function(e) {
|
||||||
|
this.snapToRest(e);
|
||||||
|
},
|
||||||
|
_initDrag: function(e) {
|
||||||
|
this.content.disableAnimation();
|
||||||
|
this._isDragging = true;
|
||||||
|
this._startX = 0;
|
||||||
|
this._offsetX = 0;
|
||||||
|
this._lastX = 0;
|
||||||
|
},
|
||||||
|
_handleDrag: function(e) {
|
||||||
|
if(!this._isDragging) {
|
||||||
|
this._initDrag(e);
|
||||||
|
|
||||||
|
this._startX = e.gesture.touches[0].pageX;
|
||||||
|
this._lastX = this._startX;
|
||||||
|
|
||||||
|
this._offsetX = this.getOpenAmount();
|
||||||
|
}
|
||||||
|
//console.log('Dragging page', this._startX, this._lastX, this._offsetX, e);
|
||||||
|
var newX = this._offsetX + (this._lastX - this._startX);
|
||||||
|
|
||||||
|
this.openAmount(newX);
|
||||||
|
|
||||||
|
this._lastX = e.gesture.touches[0].pageX;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
})(this, document, ion = this.ionic || {});
|
||||||
158
hacking/SideMenuController.unit.js
Normal file
158
hacking/SideMenuController.unit.js
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
describe('SideMenuController', function() {
|
||||||
|
var ctrl, l, r, c;
|
||||||
|
|
||||||
|
var Controller = function(opts) {
|
||||||
|
this.el = opts.el;
|
||||||
|
this.animateClass = opts.animateClass;
|
||||||
|
};
|
||||||
|
Controller.prototype = {
|
||||||
|
getTranslateX: function() {
|
||||||
|
var r = /translate3d\((-?.+)px/;
|
||||||
|
var d = r.exec(this.el.style.webkitTransform);
|
||||||
|
|
||||||
|
if(d && d.length > 0) {
|
||||||
|
return parseFloat(d[1]);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
setTranslateX: function(amount) {
|
||||||
|
this.el.style.webkitTransform = 'translate3d(' + amount + 'px, 0, 0)';
|
||||||
|
},
|
||||||
|
enableAnimation: function() {
|
||||||
|
this.el.classList.add(this.animateClass);
|
||||||
|
},
|
||||||
|
disableAnimation: function() {
|
||||||
|
this.el.classList.remove(this.animateClass);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
l = new SideMenu({ el: document.createElement('div'), width: 270 });
|
||||||
|
r = new SideMenu({ el: document.createElement('div'), width: 270 });
|
||||||
|
c = new Controller({ el: document.createElement('div') });
|
||||||
|
|
||||||
|
ctrl = new SideMenuController({
|
||||||
|
left: l,
|
||||||
|
right: r,
|
||||||
|
content: c
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Init
|
||||||
|
it('Should init', function() {
|
||||||
|
expect(ctrl.left).toBe(l);
|
||||||
|
expect(ctrl.right).toBe(r);
|
||||||
|
expect(ctrl.content).toBe(c);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Menu enable/disable
|
||||||
|
it('Should set enabled/disabled', function() {
|
||||||
|
var left = ctrl.left;
|
||||||
|
var right = ctrl.right;
|
||||||
|
left.setIsEnabled(false);
|
||||||
|
right.setIsEnabled(false);
|
||||||
|
expect(left.isEnabled).toEqual(false);
|
||||||
|
expect(right.isEnabled).toEqual(false);
|
||||||
|
left.setIsEnabled(true);
|
||||||
|
right.setIsEnabled(true);
|
||||||
|
expect(left.isEnabled).toEqual(true);
|
||||||
|
expect(right.isEnabled).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Menu widths
|
||||||
|
it('Should init widths', function() {
|
||||||
|
var left = ctrl.left;
|
||||||
|
var right = ctrl.right;
|
||||||
|
expect(left.width).toEqual(270);
|
||||||
|
expect(right.width).toEqual(270);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should have amount and percentage correct', function() {
|
||||||
|
ctrl.openAmount(l.width/2);
|
||||||
|
expect(ctrl.getOpenAmount()).toEqual(l.width/2);
|
||||||
|
expect(ctrl.getOpenPercentage()).toEqual(50);
|
||||||
|
|
||||||
|
ctrl.openAmount(l.width/4);
|
||||||
|
expect(ctrl.getOpenAmount()).toEqual(l.width/4);
|
||||||
|
expect(ctrl.getOpenPercentage()).toEqual(25);
|
||||||
|
|
||||||
|
ctrl.openAmount(-r.width/2);
|
||||||
|
expect(ctrl.getOpenAmount()).toEqual(-r.width/2);
|
||||||
|
expect(ctrl.getOpenPercentage()).toEqual(-50);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Open
|
||||||
|
it('Should toggle left and right', function() {
|
||||||
|
ctrl.toggleLeft();
|
||||||
|
expect(ctrl.getOpenPercentage()).toEqual(100);
|
||||||
|
ctrl.toggleRight();
|
||||||
|
expect(ctrl.getOpenPercentage()).toEqual(-100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Snap
|
||||||
|
it('Should snap', function() {
|
||||||
|
|
||||||
|
// Center to right, Going right, less than half, too slow (snap back)
|
||||||
|
ctrl.openAmount(10);
|
||||||
|
ctrl.snapToRest({
|
||||||
|
gesture: {
|
||||||
|
velocityX: 0,
|
||||||
|
direction: 'right'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(ctrl.getOpenPercentage()).toEqual(0);
|
||||||
|
|
||||||
|
// Right to left, Going left, more than half, too slow (snap back)
|
||||||
|
ctrl.openPercentage(51);
|
||||||
|
ctrl.snapToRest({
|
||||||
|
gesture: {
|
||||||
|
velocityX: 0,
|
||||||
|
direction: 'left'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(ctrl.getOpenPercentage()).toEqual(100);
|
||||||
|
|
||||||
|
// Right to left, Going left, less than half, too slow (snap back)
|
||||||
|
ctrl.openAmount(10);
|
||||||
|
ctrl.snapToRest({
|
||||||
|
gesture: {
|
||||||
|
velocityX: 0,
|
||||||
|
direction: 'left'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(ctrl.getOpenPercentage()).toEqual(0);
|
||||||
|
|
||||||
|
// Left to right, Going right, more than half, too slow (snap back)
|
||||||
|
ctrl.openPercentage(-51);
|
||||||
|
ctrl.snapToRest({
|
||||||
|
gesture: {
|
||||||
|
velocityX: 0,
|
||||||
|
direction: 'right'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(ctrl.getOpenPercentage()).toEqual(-100);
|
||||||
|
|
||||||
|
// Going right, more than half, or quickly (snap open)
|
||||||
|
ctrl.openPercentage(-51);
|
||||||
|
ctrl.snapToRest({
|
||||||
|
gesture: {
|
||||||
|
velocityX: 1,
|
||||||
|
direction: 'right'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(ctrl.getOpenPercentage()).toEqual(0);
|
||||||
|
|
||||||
|
// Going left, more than half, or quickly (snap open)
|
||||||
|
ctrl.openPercentage(-51);
|
||||||
|
ctrl.snapToRest({
|
||||||
|
gesture: {
|
||||||
|
velocityX: 1,
|
||||||
|
direction: 'left'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(ctrl.getOpenPercentage()).toEqual(-100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should test content drag events', function() {
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user