drawer
Version:
touch-enabled slide-in drawer ui component
187 lines (153 loc) • 3.29 kB
JavaScript
/**
* Module dependencies.
*/
var translate = require('translate');
var events = require('events');
var abs = Math.abs;
/**
* Expose `Drawer`.
*/
module.exports = Drawer;
/**
* Initialize a new Drawer.
*
* @param {Element} el
* @api public
*/
function Drawer(el, opts) {
opts = opts || {};
if (!(this instanceof Drawer)) return new Drawer(el, opts);
if (!opts.width) throw new Error('width required');
this.events = events(el, this);
this.events.bind('webkitTransitionEnd', 'ontransitionend');
this.events.bind('touchstart');
this.events.bind('touchmove');
this.events.bind('touchend');
this.width = opts.width;
this.dragThreshold = 30;
this.snapThreshold = 60;
this.el = el;
this.dx = 0;
}
/**
* Handle transitionend.
*/
Drawer.prototype.ontransitionend = function(){
resetTransition(this.el);
};
/**
* Handle touchstart.
*/
Drawer.prototype.ontouchstart = function(e){
e = e.touches[0];
this.down = { x: e.pageX, y: e.pageY };
this.x = this.isOpen ? this.width : 0;
};
/**
* Handle touchmove.
*/
Drawer.prototype.ontouchmove = function(e){
var down = this.down;
if (!down) return;
var touch = e.touches[0];
// delta
var dx = this.dx = touch.pageX - down.x;
var x = this.x + dx;
// not dragging
if (!this.dragging && abs(dx) < this.dragThreshold) return;
// open / close
if (x <= 0 || x >= this.width) return;
// translate
e.preventDefault();
translate(this.el, x, 0);
this.dragging = true;
};
/**
* Handle touchend.
*/
Drawer.prototype.ontouchend = function(e){
var dx = this.dx;
this.down = this.dx = null;
this.dragging = false;
// wrong direction
if (this.isOpen && dx > 0) return;
if (!this.isOpen && dx < 0) return;
// below threshold, revert position
if (abs(dx) < this.snapThreshold) return this.revert();
// above threshold, toggle state
this.toggle();
};
/**
* Revert state.
*
* @api public
*/
Drawer.prototype.revert = function(){
if (this.isOpen) {
this.open();
} else {
this.close();
}
};
/**
* Toggle state.
*
* @api public
*/
Drawer.prototype.toggle = function(){
if (this.isOpen) {
this.close();
} else {
this.open();
}
};
/**
* Show the drawer.
*
* @api public
*/
Drawer.prototype.hide =
Drawer.prototype.close = function(ms){
this.isOpen = false;
transition(this.el, ms);
translate(this.el, 0, 0);
};
/**
* Hide the drawer.
*
* @api public
*/
Drawer.prototype.open =
Drawer.prototype.show = function(ms){
this.isOpen = true;
transition(this.el, ms);
translate(this.el, this.width, 0);
};
/**
* Set transition.
*
* @api private
*/
function transition(el, ms){
ms = null == ms ? 300 : ms;
var s = el.style;
var ease = 'cubic-bezier(0.230, 1.000, 0.320, 1.000)'; // TODO: custom
s.webkitTransition = ms + 'ms -webkit-transform ' + ease;
s.MozTransition = ms + 'ms -moz-transform ' + ease;
s.msTransition = ms + 'ms -ms-transform ' + ease;
s.OTransition = ms + 'ms -o-transform ' + ease;
s.transition = ms + 'ms transform ' + ease;
}
/**
* Reset transition property.
*
* @api private
*/
function resetTransition(el) {
var s = el.style;
s.webkitTransition = '';
s.MozTransition = '';
s.msTransition = '';
s.OTransition = '';
s.transition = '';
}