| Current Path : /home/histoio/www/squelettes/javascript/ |
| Current File : /home/histoio/www/squelettes/javascript/jquery.parallax.js |
// jquery.parallax.js
// 2.0
// Stephen Band
//
// Project and documentation site:
// webdev.stephband.info/jparallax/
//
// Repository:
// github.com/stephband/jparallax
(function(jQuery, undefined) {
// VAR
var debug = true,
options = {
mouseport: 'body', // jQuery object or selector of DOM node to use as mouseport
xparallax: true, // boolean | 0-1 | 'npx' | 'n%'
yparallax: true, //
xorigin: 0.5, // 0-1 - Sets default alignment. Only has effect when parallax values are something other than 1 (or true, or '100%')
yorigin: 0.5, //
decay: 0.66, // 0-1 (0 instant, 1 forever) - Sets rate of decay curve for catching up with target mouse position
frameDuration: 30, // Int (milliseconds)
freezeClass: 'freeze' // String - Class added to layer when frozen
},
value = {
left: 0,
top: 0,
middle: 0.5,
center: 0.5,
right: 1,
bottom: 1
},
rpx = /^\d+\s?px$/,
rpercent = /^\d+\s?%$/,
win = jQuery(window),
doc = jQuery(document),
mouse = [0, 0];
var Timer = (function(){
var debug = false;
// Shim for requestAnimationFrame, falling back to timer. See:
// see http://paulirish.com/2011/requestanimationframe-for-smart-animating/
var requestFrame = (function(){
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(fn, node){
return window.setTimeout(function(){
fn();
}, 25);
}
);
})();
function Timer() {
var callbacks = [],
nextFrame;
function noop() {}
function frame(){
var cbs = callbacks.slice(0),
l = cbs.length,
i = -1;
if (debug) { console.log('timer frame()', l); }
while(++i < l) { cbs[i].call(this); }
requestFrame(nextFrame);
}
function start() {
if (debug) { console.log('timer start()'); }
this.start = noop;
this.stop = stop;
nextFrame = frame;
requestFrame(nextFrame);
}
function stop() {
if (debug) { console.log('timer stop()'); }
this.start = start;
this.stop = noop;
nextFrame = noop;
}
this.callbacks = callbacks;
this.start = start;
this.stop = stop;
}
Timer.prototype = {
add: function(fn) {
var callbacks = this.callbacks,
l = callbacks.length;
// Check to see if this callback is already in the list.
// Don't add it twice.
while (l--) {
if (callbacks[l] === fn) { return; }
}
this.callbacks.push(fn);
if (debug) { console.log('timer add()', this.callbacks.length); }
},
remove: function(fn) {
var callbacks = this.callbacks,
l = callbacks.length;
// Remove all instances of this callback.
while (l--) {
if (callbacks[l] === fn) { callbacks.splice(l, 1); }
}
if (debug) { console.log('timer remove()', this.callbacks.length); }
if (callbacks.length === 0) { this.stop(); }
}
};
return Timer;
})();
function parseCoord(x) {
return (rpercent.exec(x)) ? parseFloat(x)/100 : x;
}
function parseBool(x) {
return typeof x === "boolean" ? x : !!( parseFloat(x) ) ;
}
function portData(port) {
var events = {
'mouseenter.parallax': mouseenter,
'mouseleave.parallax': mouseleave
},
winEvents = {
'resize.parallax': resize
},
data = {
elem: port,
events: events,
winEvents: winEvents,
timer: new Timer()
},
layers, size, offset;
function updatePointer() {
data.pointer = getPointer(mouse, [true, true], offset, size);
}
function resize() {
size = getSize(port);
offset = getOffset(port);
data.threshold = getThreshold(size);
}
function mouseenter() {
data.timer.add(updatePointer);
}
function mouseleave(e) {
data.timer.remove(updatePointer);
data.pointer = getPointer([e.pageX, e.pageY], [true, true], offset, size);
}
win.on(winEvents);
port.on(events);
resize();
return data;
}
function getData(elem, name, fn) {
var data = elem.data(name);
if (!data) {
data = fn ? fn(elem) : {} ;
elem.data(name, data);
}
return data;
}
function getPointer(mouse, parallax, offset, size){
var pointer = [],
x = 2;
while (x--) {
pointer[x] = (mouse[x] - offset[x]) / size[x] ;
pointer[x] = pointer[x] < 0 ? 0 : pointer[x] > 1 ? 1 : pointer[x] ;
}
return pointer;
}
function getSize(elem) {
return [elem.width(), elem.height()];
}
function getOffset(elem) {
var offset = elem.offset() || {left: 0, top: 0},
borderLeft = elem.css('borderLeftStyle') === 'none' ? 0 : parseInt(elem.css('borderLeftWidth'), 10),
borderTop = elem.css('borderTopStyle') === 'none' ? 0 : parseInt(elem.css('borderTopWidth'), 10),
paddingLeft = parseInt(elem.css('paddingLeft'), 10),
paddingTop = parseInt(elem.css('paddingTop'), 10);
return [offset.left + borderLeft + paddingLeft, offset.top + borderTop + paddingTop];
}
function getThreshold(size) {
return [1/size[0], 1/size[1]];
}
function layerSize(elem, x, y) {
return [x || elem.outerWidth(), y || elem.outerHeight()];
}
function layerOrigin(xo, yo) {
var o = [xo, yo],
i = 2,
origin = [];
while (i--) {
origin[i] = typeof o[i] === 'string' ?
o[i] === undefined ?
1 :
value[origin[i]] || parseCoord(origin[i]) :
o[i] ;
}
return origin;
}
function layerPx(xp, yp) {
return [rpx.test(xp), rpx.test(yp)];
}
function layerParallax(xp, yp, px) {
var p = [xp, yp],
i = 2,
parallax = [];
while (i--) {
parallax[i] = px[i] ?
parseInt(p[i], 10) :
parallax[i] = p[i] === true ? 1 : parseCoord(p[i]) ;
}
return parallax;
}
function layerOffset(parallax, px, origin, size) {
var i = 2,
offset = [];
while (i--) {
offset[i] = px[i] ?
origin[i] * (size[i] - parallax[i]) :
parallax[i] ? origin[i] * ( 1 - parallax[i] ) : 0 ;
}
return offset;
}
function layerPosition(px, origin) {
var i = 2,
position = [];
while (i--) {
if (px[i]) {
// Set css position constant
position[i] = origin[i] * 100 + '%';
}
else {
}
}
return position;
}
function layerPointer(elem, parallax, px, offset, size) {
var viewport = elem.offsetParent(),
pos = elem.position(),
position = [],
pointer = [],
i = 2;
// Reverse calculate ratio from elem's current position
while (i--) {
position[i] = px[i] ?
// TODO: reverse calculation for pixel case
0 :
pos[i === 0 ? 'left' : 'top'] / (viewport[i === 0 ? 'outerWidth' : 'outerHeight']() - size[i]) ;
pointer[i] = (position[i] - offset[i]) / parallax[i] ;
}
return pointer;
}
function layerCss(parallax, px, offset, size, position, pointer) {
var pos = [],
cssPosition,
cssMargin,
x = 2,
css = {};
while (x--) {
if (parallax[x]) {
pos[x] = parallax[x] * pointer[x] + offset[x];
// We're working in pixels
if (px[x]) {
cssPosition = position[x];
cssMargin = pos[x] * -1;
}
// We're working by ratio
else {
cssPosition = pos[x] * 100 + '%';
cssMargin = pos[x] * size[x] * -1;
}
// Fill in css object
if (x === 0) {
css.left = cssPosition;
css.marginLeft = cssMargin;
}
else {
css.top = cssPosition;
css.marginTop = cssMargin;
}
}
}
return css;
}
function pointerOffTarget(targetPointer, prevPointer, threshold, decay, parallax, targetFn, updateFn) {
var pointer, x;
if ((!parallax[0] || Math.abs(targetPointer[0] - prevPointer[0]) < threshold[0]) &&
(!parallax[1] || Math.abs(targetPointer[1] - prevPointer[1]) < threshold[1])) {
// Pointer has hit the target
if (targetFn) { targetFn(); }
return updateFn(targetPointer);
}
// Pointer is nowhere near the target
pointer = [];
x = 2;
while (x--) {
if (parallax[x]) {
pointer[x] = targetPointer[x] + decay * (prevPointer[x] - targetPointer[x]);
}
}
return updateFn(pointer);
}
function pointerOnTarget(targetPointer, prevPointer, threshold, decay, parallax, targetFn, updateFn) {
// Don't bother updating if the pointer hasn't changed.
if (targetPointer[0] === prevPointer[0] && targetPointer[1] === prevPointer[1]) {
return;
}
return updateFn(targetPointer);
}
function unport(elem, events, winEvents) {
elem.off(events).removeData('parallax_port');
win.off(winEvents);
}
function unparallax(node, port, events) {
port.elem.off(events);
// Remove this node from layers
port.layers = port.layers.not(node);
// If port.layers is empty, destroy the port
if (port.layers.length === 0) {
unport(port.elem, port.events, port.winEvents);
}
}
function unstyle(parallax) {
var css = {};
if (parallax[0]) {
css.left = '';
css.marginLeft = '';
}
if (parallax[1]) {
css.top = '';
css.marginTop = '';
}
elem.css(css);
}
jQuery.fn.parallax = function(o){
var options = jQuery.extend({}, jQuery.fn.parallax.options, o),
args = arguments,
elem = options.mouseport instanceof jQuery ?
options.mouseport :
jQuery(options.mouseport) ,
port = getData(elem, 'parallax_port', portData),
timer = port.timer;
return this.each(function(i) {
var node = this,
elem = jQuery(this),
opts = args[i + 1] ? jQuery.extend({}, options, args[i + 1]) : options,
decay = opts.decay,
size = layerSize(elem, opts.width, opts.height),
origin = layerOrigin(opts.xorigin, opts.yorigin),
px = layerPx(opts.xparallax, opts.yparallax),
parallax = layerParallax(opts.xparallax, opts.yparallax, px),
offset = layerOffset(parallax, px, origin, size),
position = layerPosition(px, origin),
pointer = layerPointer(elem, parallax, px, offset, size),
pointerFn = pointerOffTarget,
targetFn = targetInside,
events = {
'mouseenter.parallax': function mouseenter(e) {
pointerFn = pointerOffTarget;
targetFn = targetInside;
timer.add(frame);
timer.start();
},
'mouseleave.parallax': function mouseleave(e) {
// Make the layer come to rest at it's limit with inertia
pointerFn = pointerOffTarget;
// Stop the timer when the the pointer hits target
targetFn = targetOutside;
}
};
function updateCss(newPointer) {
var css = layerCss(parallax, px, offset, size, position, newPointer);
elem.css(css);
pointer = newPointer;
}
function frame() {
pointerFn(port.pointer, pointer, port.threshold, decay, parallax, targetFn, updateCss);
}
function targetInside() {
// Pointer hits the target pointer inside the port
pointerFn = pointerOnTarget;
}
function targetOutside() {
// Pointer hits the target pointer outside the port
timer.remove(frame);
}
if (jQuery.data(node, 'parallax')) {
elem.unparallax();
}
jQuery.data(node, 'parallax', {
port: port,
events: events,
parallax: parallax
});
port.elem.on(events);
port.layers = port.layers? port.layers.add(node): jQuery(node);
/*function freeze() {
freeze = true;
}
function unfreeze() {
freeze = false;
}*/
/*jQuery.event.add(this, 'freeze.parallax', freeze);
jQuery.event.add(this, 'unfreeze.parallax', unfreeze);*/
});
};
jQuery.fn.unparallax = function(bool) {
return this.each(function() {
var data = jQuery.data(this, 'parallax');
// This elem is not parallaxed
if (!data) { return; }
jQuery.removeData(this, 'parallax');
unparallax(this, data.port, data.events);
if (bool) { unstyle(data.parallax); }
});
};
jQuery.fn.parallax.options = options;
// Pick up and store mouse position on document: IE does not register
// mousemove on window.
doc.on('mousemove.parallax', function(e){
mouse = [e.pageX, e.pageY];
});
}(jQuery));