1// jquery.parallax.js 2// 2.0 3// Stephen Band 4// 5// Project and documentation site: 6// webdev.stephband.info/jparallax/ 7// 8// Repository: 9// github.com/stephband/jparallax 10 11(function(jQuery, undefined) { 12 // VAR 13 var debug = true, 14 15 options = { 16 mouseport: 'body', // jQuery object or selector of DOM node to use as mouseport 17 xparallax: true, // boolean | 0-1 | 'npx' | 'n%' 18 yparallax: true, // 19 xorigin: 0.5, // 0-1 - Sets default alignment. Only has effect when parallax values are something other than 1 (or true, or '100%') 20 yorigin: 0.5, // 21 decay: 0.66, // 0-1 (0 instant, 1 forever) - Sets rate of decay curve for catching up with target mouse position 22 frameDuration: 30, // Int (milliseconds) 23 freezeClass: 'freeze' // String - Class added to layer when frozen 24 }, 25 26 value = { 27 left: 0, 28 top: 0, 29 middle: 0.5, 30 center: 0.5, 31 right: 1, 32 bottom: 1 33 }, 34 35 rpx = /^\d+\s?px$/, 36 rpercent = /^\d+\s?%$/, 37 38 win = jQuery(window), 39 doc = jQuery(document), 40 mouse = [0, 0]; 41 42 var Timer = (function(){ 43 var debug = false; 44 45 // Shim for requestAnimationFrame, falling back to timer. See: 46 // see http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 47 var requestFrame = (function(){ 48 return ( 49 window.requestAnimationFrame || 50 window.webkitRequestAnimationFrame || 51 window.mozRequestAnimationFrame || 52 window.oRequestAnimationFrame || 53 window.msRequestAnimationFrame || 54 function(fn, node){ 55 return window.setTimeout(function(){ 56 fn(); 57 }, 25); 58 } 59 ); 60 })(); 61 62 function Timer() { 63 var callbacks = [], 64 nextFrame; 65 66 function noop() {} 67 68 function frame(){ 69 var cbs = callbacks.slice(0), 70 l = cbs.length, 71 i = -1; 72 73 if (debug) { console.log('timer frame()', l); } 74 75 while(++i < l) { cbs[i].call(this); } 76 requestFrame(nextFrame); 77 } 78 79 function start() { 80 if (debug) { console.log('timer start()'); } 81 this.start = noop; 82 this.stop = stop; 83 nextFrame = frame; 84 requestFrame(nextFrame); 85 } 86 87 function stop() { 88 if (debug) { console.log('timer stop()'); } 89 this.start = start; 90 this.stop = noop; 91 nextFrame = noop; 92 } 93 94 this.callbacks = callbacks; 95 this.start = start; 96 this.stop = stop; 97 } 98 99 Timer.prototype = { 100 add: function(fn) { 101 var callbacks = this.callbacks, 102 l = callbacks.length; 103 104 // Check to see if this callback is already in the list. 105 // Don't add it twice. 106 while (l--) { 107 if (callbacks[l] === fn) { return; } 108 } 109 110 this.callbacks.push(fn); 111 if (debug) { console.log('timer add()', this.callbacks.length); } 112 }, 113 114 remove: function(fn) { 115 var callbacks = this.callbacks, 116 l = callbacks.length; 117 118 // Remove all instances of this callback. 119 while (l--) { 120 if (callbacks[l] === fn) { callbacks.splice(l, 1); } 121 } 122 123 if (debug) { console.log('timer remove()', this.callbacks.length); } 124 125 if (callbacks.length === 0) { this.stop(); } 126 } 127 }; 128 129 return Timer; 130 })(); 131 132 function parseCoord(x) { 133 return (rpercent.exec(x)) ? parseFloat(x)/100 : x; 134 } 135 136 function parseBool(x) { 137 return typeof x === "boolean" ? x : !!( parseFloat(x) ) ; 138 } 139 140 function portData(port) { 141 var events = { 142 'mouseenter.parallax': mouseenter, 143 'mouseleave.parallax': mouseleave 144 }, 145 winEvents = { 146 'resize.parallax': resize 147 }, 148 data = { 149 elem: port, 150 events: events, 151 winEvents: winEvents, 152 timer: new Timer() 153 }, 154 layers, size, offset; 155 156 function updatePointer() { 157 data.pointer = getPointer(mouse, [true, true], offset, size); 158 } 159 160 function resize() { 161 size = getSize(port); 162 offset = getOffset(port); 163 data.threshold = getThreshold(size); 164 } 165 166 function mouseenter() { 167 data.timer.add(updatePointer); 168 } 169 170 function mouseleave(e) { 171 data.timer.remove(updatePointer); 172 data.pointer = getPointer([e.pageX, e.pageY], [true, true], offset, size); 173 } 174 175 win.on(winEvents); 176 port.on(events); 177 178 resize(); 179 180 return data; 181 } 182 183 function getData(elem, name, fn) { 184 var data = elem.data(name); 185 186 if (!data) { 187 data = fn ? fn(elem) : {} ; 188 elem.data(name, data); 189 } 190 191 return data; 192 } 193 194 function getPointer(mouse, parallax, offset, size){ 195 var pointer = [], 196 x = 2; 197 198 while (x--) { 199 pointer[x] = (mouse[x] - offset[x]) / size[x] ;
200 pointer[x] = pointer[x] < 0 ? 0 : pointer[x] > 1 ? 1 : pointer[x] ; 201 } 202 203 return pointer; 204 } 205 206 function getSize(elem) { 207 return [elem.width(), elem.height()]; 208 } 209 210 function getOffset(elem) { 211 var offset = elem.offset() || {left: 0, top: 0}, 212 borderLeft = elem.css('borderLeftStyle') === 'none' ? 0 : parseInt(elem.css('borderLeftWidth'), 10), 213 borderTop = elem.css('borderTopStyle') === 'none' ? 0 : parseInt(elem.css('borderTopWidth'), 10), 214 paddingLeft = parseInt(elem.css('paddingLeft'), 10), 215 paddingTop = parseInt(elem.css('paddingTop'), 10); 216 217 return [offset.left + borderLeft + paddingLeft, offset.top + borderTop + paddingTop]; 218 } 219 220 function getThreshold(size) { 221 return [1/size[0], 1/size[1]]; 222 } 223 224 function layerSize(elem, x, y) { 225 return [x || elem.outerWidth(), y || elem.outerHeight()]; 226 } 227 228 function layerOrigin(xo, yo) { 229 var o = [xo, yo], 230 i = 2, 231 origin = []; 232 233 while (i--) { 234 origin[i] = typeof o[i] === 'string' ? 235 o[i] === undefined ? 236 1 : 237 value[origin[i]] || parseCoord(origin[i]) : 238 o[i] ; 239 } 240 241 return origin; 242 } 243 244 function layerPx(xp, yp) { 245 return [rpx.test(xp), rpx.test(yp)]; 246 } 247 248 function layerParallax(xp, yp, px) { 249 var p = [xp, yp], 250 i = 2, 251 parallax = []; 252 253 while (i--) { 254 parallax[i] = px[i] ? 255 parseInt(p[i], 10) : 256 parallax[i] = p[i] === true ? 1 : parseCoord(p[i]) ; 257 } 258 259 return parallax; 260 } 261 262 function layerOffset(parallax, px, origin, size) { 263 var i = 2, 264 offset = []; 265 266 while (i--) { 267 offset[i] = px[i] ? 268 origin[i] * (size[i] - parallax[i]) : 269 parallax[i] ? origin[i] * ( 1 - parallax[i] ) : 0 ; 270 } 271 272 return offset; 273 } 274 275 function layerPosition(px, origin) { 276 var i = 2, 277 position = []; 278 279 while (i--) { 280 if (px[i]) { 281 // Set css position constant 282 position[i] = origin[i] * 100 + '%'; 283 } 284 else { 285 286 } 287 } 288 289 return position; 290 } 291 292 function layerPointer(elem, parallax, px, offset, size) { 293 var viewport = elem.offsetParent(), 294 pos = elem.position(), 295 position = [], 296 pointer = [], 297 i = 2; 298 299 // Reverse calculate ratio from elem's current position 300 while (i--) { 301 position[i] = px[i] ? 302 // TODO: reverse calculation for pixel case 303 0 : 304 pos[i === 0 ? 'left' : 'top'] / (viewport[i === 0 ? 'outerWidth' : 'outerHeight']() - size[i]) ; 305 306 pointer[i] = (position[i] - offset[i]) / parallax[i] ; 307 } 308 309 return pointer; 310 } 311 312 function layerCss(parallax, px, offset, size, position, pointer) { 313 var pos = [], 314 cssPosition, 315 cssMargin, 316 x = 2, 317 css = {}; 318 319 while (x--) { 320 if (parallax[x]) { 321 pos[x] = parallax[x] * pointer[x] + offset[x]; 322 323 // We're working in pixels 324 if (px[x]) { 325 cssPosition = position[x]; 326 cssMargin = pos[x] * -1; 327 } 328 // We're working by ratio 329 else { 330 cssPosition = pos[x] * 100 + '%'; 331 cssMargin = pos[x] * size[x] * -1; 332 } 333 334 // Fill in css object 335 if (x === 0) { 336 css.left = cssPosition; 337 css.marginLeft = cssMargin; 338 } 339 else { 340 css.top = cssPosition; 341 css.marginTop = cssMargin; 342 } 343 } 344 } 345 346 return css; 347 } 348 349 function pointerOffTarget(targetPointer, prevPointer, threshold, decay, parallax, targetFn, updateFn) { 350 var pointer, x; 351 352 if ((!parallax[0] || Math.abs(targetPointer[0] - prevPointer[0]) < threshold[0]) && 353 (!parallax[1] || Math.abs(targetPointer[1] - prevPointer[1]) < threshold[1])) { 354 // Pointer has hit the target 355 if (targetFn) { targetFn(); } 356 return updateFn(targetPointer); 357 } 358 359 // Pointer is nowhere near the target 360 pointer = []; 361 x = 2; 362 363 while (x--) { 364 if (parallax[x]) {
365 pointer[x] = targetPointer[x] + decay * (prevPointer[x] - targetPointer[x]); 366 } 367 } 368 369 return updateFn(pointer); 370 } 371 372 function pointerOnTarget(targetPointer, prevPointer, threshold, decay, parallax, targetFn, updateFn) { 373 // Don't bother updating if the pointer hasn't changed. 374 if (targetPointer[0] === prevPointer[0] && targetPointer[1] === prevPointer[1]) { 375 return; 376 } 377 378 return updateFn(targetPointer); 379 } 380 381 function unport(elem, events, winEvents) { 382 elem.off(events).removeData('parallax_port'); 383 win.off(winEvents); 384 } 385 386 function unparallax(node, port, events) { 387 port.elem.off(events); 388 389 // Remove this node from layers 390 port.layers = port.layers.not(node); 391 392 // If port.layers is empty, destroy the port 393 if (port.layers.length === 0) { 394 unport(port.elem, port.events, port.winEvents); 395 } 396 } 397 398 function unstyle(parallax) { 399 var css = {}; 400 401 if (parallax[0]) { 402 css.left = ''; 403 css.marginLeft = ''; 404 } 405 406 if (parallax[1]) { 407 css.top = ''; 408 css.marginTop = ''; 409 } 410 411 elem.css(css); 412 } 413 414 jQuery.fn.parallax = function(o){ 415 console.log(2); 416 var options = jQuery.extend({}, jQuery.fn.parallax.options, o), 417 args = arguments, 418 elem = options.mouseport instanceof jQuery ? 419 options.mouseport : 420 jQuery(options.mouseport) , 421 port = getData(elem, 'parallax_port', portData), 422 timer = port.timer; 423 424 return this.each(function(i) { 425 var node = this, 426 elem = jQuery(this), 427 opts = args[i + 1] ? jQuery.extend({}, options, args[i + 1]) : options, 428 decay = opts.decay, 429 size = layerSize(elem, opts.width, opts.height), 430 origin = layerOrigin(opts.xorigin, opts.yorigin), 431 px = layerPx(opts.xparallax, opts.yparallax), 432 parallax = layerParallax(opts.xparallax, opts.yparallax, px), 433 offset = layerOffset(parallax, px, origin, size), 434 position = layerPosition(px, origin), 435 pointer = layerPointer(elem, parallax, px, offset, size), 436 pointerFn = pointerOffTarget, 437 targetFn = targetInside, 438 events = { 439 'mouseenter.parallax': function mouseenter(e) { 440 pointerFn = pointerOffTarget; 441 targetFn = targetInside; 442 timer.add(frame); 443 timer.start(); 444 }, 445 'mouseleave.parallax': function mouseleave(e) { 446 // Make the layer come to rest at it's limit with inertia 447 pointerFn = pointerOffTarget; 448 // Stop the timer when the the pointer hits target 449 targetFn = targetOutside; 450 } 451 }; 452 453 function updateCss(newPointer) { 454 var css = layerCss(parallax, px, offset, size, position, newPointer); 455 elem.css(css); 456 pointer = newPointer; 457 } 458 459 function frame() { 460 pointerFn(port.pointer, pointer, port.threshold, decay, parallax, targetFn, updateCss); 461 } 462 463 function targetInside() { 464 // Pointer hits the target pointer inside the port 465 pointerFn = pointerOnTarget; 466 } 467 468 function targetOutside() { 469 // Pointer hits the target pointer outside the port 470 timer.remove(frame); 471 } 472 473 474 if (jQuery.data(node, 'parallax')) { 475 elem.unparallax(); 476 } 477 478 jQuery.data(node, 'parallax', { 479 port: port, 480 events: events, 481 parallax: parallax 482 }); 483 484 port.elem.on(events); 485 port.layers = port.layers? port.layers.add(node): jQuery(node); 486 487 /*function freeze() { 488 freeze = true; 489 } 490 491 function unfreeze() { 492 freeze = false; 493 }*/ 494 495 /*jQuery.event.add(this, 'freeze.parallax', freeze); 496 jQuery.event.add(this, 'unfreeze.parallax', unfreeze);*/ 497 }); 498 }; 499 500 jQuery.fn.unparallax = function(bool) { 501 return this.each(function() { 502 var data = jQuery.data(this, 'parallax'); 503 504 // This elem is not parallaxed 505 if (!data) { return; } 506 507 jQuery.removeData(this, 'parallax'); 508 unparallax(this, data.port, data.events); 509 if (bool) { unstyle(data.parallax); } 510 }); 511 }; 512 513 jQuery.fn.parallax.options = options; 514 515 // Pick up and store mouse position on document: IE does not register 516 // mousemove on window. 517 doc.on('mousemove.parallax', function(e){
518 mouse = [e.pageX, e.pageY]; 519 }); 520}(jQuery));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.