page-slider
Version:
A little library to add CSS transitions between pages in a mobile web app.
271 lines (214 loc) • 8.52 kB
JavaScript
/**
* Helper to slide a new page in the app.
* This helper only works well if the container is at 100% height/width and overflow hidden.
*
* Based on : https://github.com/ccoenraets/directory-backbone-topcoat-require
*
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node.
module.exports = factory(require('jquery'));
} else {
// Browser globals (root is window)
root.PageSlider = factory(root.jQuery);
}
}(this, function ($) {
'use strict';
var PageSlider = function ($container) {
this.$container = $container;
// The container need specific style
$container.css({
height: '100%',
width: '100%',
overflow: 'hidden',
});
};
PageSlider.prototype = {
constructor: PageSlider,
$container: null,
transitionsEnabled: true,
transitionDurationMs: 300,
stateHistory: [],
$currentPage: null,
/**
* Set a page position via translate3d
* @param {jquery} $page Page that we want to Position
* @param {String} newLocation New position ('left', 'right' or null (default))
*/
_setPagePosition: function ($page, newLocation) {
var newPosition = '0px';
switch (newLocation) {
case 'left':
newPosition = '-100%';
break;
case 'right':
newPosition = '100%';
break;
}
var transform = 'translate3d(' + newPosition + ', 0px , 0px)';
$page.css({
'webkitTransform': transform,
'transform': transform,
});
},
_jQueryArrayToCollection: function (jqueryArray) {
if (jqueryArray instanceof $) {
return jqueryArray;
}
var $collection = $();
jqueryArray.forEach(function ($el) {
$collection = $collection.add($el);
});
return $collection;
},
/**
* Enable css transition on the given page.
*/
_enableTransitionOnPages: function ($pages) {
$pages = this._jQueryArrayToCollection($pages);
$pages.css({
'webkitTransitionDuration': this.transitionDurationMs + 'ms',
'transitionDuration': this.transitionDurationMs + 'ms',
});
},
/**
* Disable css transition on the given page.
*/
_disableTransitionOnPages: function ($pages) {
$pages = this._jQueryArrayToCollection($pages);
$pages.css({
'webkitTransform': 'none',
'transform': 'none',
'webkitTransitionDuration': '0s',
'transitionDuration': '0s',
});
},
/**
* Set transition duration in milliseconds
*/
setTransitionDurationMs: function (durationMs) {
this.transitionDurationMs = durationMs;
},
/**
* Disable css transition on page
*/
disableTransitions: function () {
this.transitionsEnabled = false;
},
/**
* Enable css transition on page
*/
enableTransitions: function () {
this.transitionsEnabled = true;
},
getSlideOriginForLocation: function (hash) {
var historyLength = this.stateHistory.length;
// First page and page reload don't get sliding animation
if (historyLength === 0 || hash === this.stateHistory[historyLength - 1]) {
return null;
}
else if (hash === this.stateHistory[historyLength - 2]) {
return 'left';
}
else {
return 'right';
}
},
/**
* Return the type of slide (null, 'left' or 'right')
*/
getNextSlideOrigin: function () {
return this.getSlideOriginForLocation(location.hash);
},
/**
* Use this function if you want PageSlider to automatically determine
* the sliding direction based on the state history.
* afterTransition function is called when the transition ends
*/
slidePage: function ($newPage, options) {
var state = location.hash;
var slideBehaviour = this.getNextSlideOrigin();
switch (slideBehaviour) {
case 'left':
this.stateHistory.pop();
this.slidePageFrom($newPage, 'left', options);
break;
case 'right':
this.stateHistory.push(state);
this.slidePageFrom($newPage, 'right', options);
break;
default:
this.stateHistory.push(state);
this.slidePageFrom($newPage, null, options);
break;
}
},
/**
* Use this function directly if you want to control the sliding direction outside PageSlider
* @param {$} $newPage The new page to slide in
* @param {String} origin Origin of the slide ('left', 'right', or null)
* @param {function} options
* beforeTransition: Called before the transition, after the page is added
* to the DOM.
* afterTransition: Called when the slide end
* or immediately if there is no transition.
* A boolean is passed to the callback : true if we just slide
* in the very first page.
*
*/
slidePageFrom: function ($newPage, origin, options) {
var that = this;
options = options || {};
options.beforeTransition = options.beforeTransition || $.noop;
options.afterTransition = options.afterTransition || $.noop;
var $oldPage = this.$currentPage;
var isFirstPageSlide = !$oldPage;
$newPage.addClass('page');
this.$container.append($newPage);
options.beforeTransition();
// First loaded page (no old page) or no transition
if (isFirstPageSlide || !origin || !this.transitionsEnabled) {
// Disable transition
this._disableTransitionOnPages($newPage);
// Remove old page if it exists
if ($oldPage) {
$oldPage.remove();
}
this.$currentPage = $newPage;
// We call the transition end callback anyway
options.afterTransition(isFirstPageSlide);
return;
}
// Position the page at the starting position of the animation
this._setPagePosition($newPage, origin);
// Shim transitionend if it's not fired
var shimTransitionEnd = setTimeout(function() {
onTransitionEnd();
}, this.transitionDurationMs + 100);
this.$currentPage.one('transitionend webkitTransitionEnd', function () {
onTransitionEnd();
});
// Position the new page and the current page at the ending position of their animation
// And enable their animation via transition
this._enableTransitionOnPages([$newPage, $oldPage]);
// Force reflow. More information here:
// http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/
/*jshint -W030*/
this.$container[0].offsetWidth;
this._setPagePosition($newPage, 'center');
this._setPagePosition($oldPage, (origin === 'left' ? 'right' : 'left'));
this.$currentPage = $newPage;
var onTransitionEnd = function () {
that._disableTransitionOnPages(that.$currentPage);
that.$container.find('> .page:not(:last)').remove();
clearTimeout(shimTransitionEnd);
options.afterTransition(false);
};
},
};
return PageSlider;
}));