vendor: 17,819 bytes, lines 1-497
1 2// ------------------------------------------ 3// Rellax.js 4// Buttery smooth parallax library 5// Copyright (c) 2016 Moe Amaya (@moeamaya) 6// MIT license 7// 8// Thanks to Paraxify.js and Jaime Cabllero 9// for parallax concepts 10// ------------------------------------------ 11 12(function (root, factory) { 13 if (typeof define === 'function' && define.amd) { 14 // AMD. Register as an anonymous module. 15 define([], factory); 16 } else if (typeof module === 'object' && module.exports) { 17 // Node. Does not work with strict CommonJS, but 18 // only CommonJS-like environments that support module.exports, 19 // like Node. 20 module.exports = factory(); 21 } else { 22 // Browser globals (root is window) 23 root.Rellax = factory(); 24 } 25}(typeof window !== "undefined" ? window : global, function () { 26 var Rellax = function(el, options){ 27 "use strict"; 28 29 var self = Object.create(Rellax.prototype); 30 31 var posY = 0; 32 var screenY = 0; 33 var posX = 0; 34 var screenX = 0; 35 var blocks = []; 36 var pause = true; 37 38 // check what requestAnimationFrame to use, and if 39 // it's not supported, use the onscroll event 40 var loop = window.requestAnimationFrame || 41 window.webkitRequestAnimationFrame || 42 window.mozRequestAnimationFrame || 43 window.msRequestAnimationFrame || 44 window.oRequestAnimationFrame || 45 function(callback){ return setTimeout(callback, 1000 / 60); }; 46 47 // store the id for later use 48 var loopId = null; 49 50 // Test via a getter in the options object to see if the passive property is accessed 51 var supportsPassive = false; 52 try { 53 var opts = Object.defineProperty({}, 'passive', { 54 get: function() { 55 supportsPassive = true; 56 } 57 }); 58 window.addEventListener("testPassive", null, opts); 59 window.removeEventListener("testPassive", null, opts); 60 } catch (e) {} 61 62 // check what cancelAnimation method to use 63 var clearLoop = window.cancelAnimationFrame || window.mozCancelAnimationFrame || clearTimeout; 64 65 // check which transform property to use 66 var transformProp = window.transformProp || (function(){ 67 var testEl = document.createElement('div'); 68 if (testEl.style.transform === null) { 69 var vendors = ['Webkit', 'Moz', 'ms']; 70 for (var vendor in vendors) { 71 if (testEl.style[ vendors[vendor] + 'Transform' ] !== undefined) { 72 return vendors[vendor] + 'Transform'; 73 } 74 } 75 } 76 return 'transform'; 77 })(); 78 79 // Default Settings 80 self.options = { 81 speed: -2, 82 verticalSpeed: null, 83 horizontalSpeed: null, 84 breakpoints: [576, 768, 1201], 85 center: false, 86 wrapper: null, 87 relativeToWrapper: false, 88 round: true, 89 vertical: true, 90 horizontal: false, 91 verticalScrollAxis: "y", 92 horizontalScrollAxis: "x", 93 callback: function() {}, 94 }; 95 96 // User defined options (might have more in the future) 97 if (options){ 98 Object.keys(options).forEach(function(key){ 99 self.options[key] = options[key]; 100 }); 101 } 102 103 function validateCustomBreakpoints () { 104 if (self.options.breakpoints.length === 3 && Array.isArray(self.options.breakpoints)) { 105 var isAscending = true; 106 var isNumerical = true; 107 var lastVal; 108 self.options.breakpoints.forEach(function (i) { 109 if (typeof i !== 'number') isNumerical = false; 110 if (lastVal !== null) { 111 if (i < lastVal) isAscending = false; 112 } 113 lastVal = i; 114 }); 115 if (isAscending && isNumerical) return; 116 } 117 // revert defaults if set incorrectly 118 self.options.breakpoints = [576, 768, 1201]; 119 console.warn("Rellax: You must pass an array of 3 numbers in ascending order to the breakpoints option. Defaults reverted"); 120 } 121 122 if (options && options.breakpoints) { 123 validateCustomBreakpoints(); 124 } 125 126 // By default, rellax class 127 if (!el) { 128 el = '.rellax'; 129 } 130 131 // check if el is a className or a node 132 var elements = typeof el === 'string' ? document.querySelectorAll(el) : [el]; 133 134 // Now query selector 135 if (elements.length > 0) { 136 self.elems = elements; 137 } 138 139 // The elements don't exist 140 else { 141 console.warn("Rellax: The elements you're trying to select don't exist."); 142 return; 143 } 144 145 // Has a wrapper and it exists 146 if (self.options.wrapper) { 147 if (!self.options.wrapper.nodeType) { 148 var wrapper = document.querySelector(self.options.wrapper); 149 150 if (wrapper) { 151 self.options.wrapper = wrapper; 152 } else { 153 console.warn("Rellax: The wrapper you're trying to use doesn't exist."); 154 return; 155 } 156 } 157 } 158 159 // set a placeholder for the current breakpoint 160 var currentBreakpoint; 161 162 // helper to determine current breakpoint 163 var getCurrentBreakpoint = function (w) { 164 var bp = self.options.breakpoints; 165 if (w < bp[0]) return 'xs'; 166 if (w >= bp[0] && w < bp[1]) return 'sm'; 167 if (w >= bp[1] && w < bp[2]) return 'md'; 168 return 'lg'; 169 }; 170 171 // Get and cache initial position of all elements 172 var cacheBlocks = function() { 173 for (var i = 0; i < self.elems.length; i++){ 174 var block = createBlock(self.elems[i]); 175 blocks.push(block); 176 } 177 }; 178 179 180 // Let's kick this script off 181 // Build array for cached element values 182 var init = function() { 183 for (var i = 0; i < blocks.length; i++){ 184 self.elems[i].style.cssText = blocks[i].style; 185 } 186 187 blocks = []; 188 189 screenY = window.innerHeight; 190 screenX = window.innerWidth; 191 currentBreakpoint = getCurrentBreakpoint(screenX); 192 193 setPosition(); 194 195 cacheBlocks(); 196 197 animate(); 198 199 // If paused, unpause and set listener for window resizing events 200 if (pause) { 201 window.addEventListener('resize', init); 202 pause = false; 203 // Start the loop 204 update(); 205 } 206 }; 207 208 // We want to cache the parallax blocks' 209 // values: base, top, height, speed 210 // el: is dom object, return: el cache values 211 var createBlock = function(el) { 212 var dataPercentage = el.getAttribute( 'data-rellax-percentage' ); 213 var dataSpeed = el.getAttribute( 'data-rellax-speed' ); 214 var dataXsSpeed = el.getAttribute( 'data-rellax-xs-speed' ); 215 var dataMobileSpeed = el.getAttribute( 'data-rellax-mobile-speed' ); 216 var dataTabletSpeed = el.getAttribute( 'data-rellax-tablet-speed' ); 217 var dataDesktopSpeed = el.getAttribute( 'data-rellax-desktop-speed' ); 218 var dataVerticalSpeed = el.getAttribute('data-rellax-vertical-speed'); 219 var dataHorizontalSpeed = el.getAttribute('data-rellax-horizontal-speed'); 220 var dataVericalScrollAxis = el.getAttribute('data-rellax-vertical-scroll-axis'); 221 var dataHorizontalScrollAxis = el.getAttribute('data-rellax-horizontal-scroll-axis'); 222 var dataZindex = el.getAttribute( 'data-rellax-zindex' ) || 0; 223 var dataMin = el.getAttribute( 'data-rellax-min' ); 224 var dataMax = el.getAttribute( 'data-rellax-max' ); 225 var dataMinX = el.getAttribute('data-rellax-min-x'); 226 var dataMaxX = el.getAttribute('data-rellax-max-x'); 227 var dataMinY = el.getAttribute('data-rellax-min-y'); 228 var dataMaxY = el.getAttribute('data-rellax-max-y'); 229 var mapBreakpoints; 230 var breakpoints = true; 231 232 if (!dataXsSpeed && !dataMobileSpeed && !dataTabletSpeed && !dataDesktopSpeed) { 233 breakpoints = false; 234 } else { 235 mapBreakpoints = { 236 'xs': dataXsSpeed, 237 'sm': dataMobileSpeed, 238 'md': dataTabletSpeed, 239 'lg': dataDesktopSpeed 240 }; 241 } 242 243 // initializing at scrollY = 0 (top of browser), scrollX = 0 (left of browser) 244 // ensures elements are positioned based on HTML layout. 245 // 246 // If the element has the percentage attribute, the posY and posX needs to be 247 // the current scroll position's value, so that the elements are still positioned based on HTML layout 248 var wrapperPosY = self.options.wrapper ? self.options.wrapper.scrollTop : (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); 249 // If the option relativeToWrapper is true, use the wrappers offset to top, subtracted from the current page scroll. 250 if (self.options.relativeToWrapper) { 251 var scrollPosY = (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); 252 wrapperPosY = scrollPosY - self.options.wrapper.offsetTop; 253 } 254 var posY = self.options.vertical ? ( dataPercentage || self.options.center ? wrapperPosY : 0 ) : 0; 255 var posX = self.options.horizontal ? ( dataPercentage || self.options.center ? self.options.wrapper ? self.options.wrapper.scrollLeft : (window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft) : 0 ) : 0; 256 257 var blockTop = posY + el.getBoundingClientRect().top; 258 var blockHeight = el.clientHeight || el.offsetHeight || el.scrollHeight; 259 260 var blockLeft = posX + el.getBoundingClientRect().left; 261 var blockWidth = el.clientWidth || el.offsetWidth || el.scrollWidth; 262 263 // apparently parallax equation everyone uses 264 var percentageY = dataPercentage ? dataPercentage : (posY - blockTop + screenY) / (blockHeight + screenY); 265 var percentageX = dataPercentage ? dataPercentage : (posX - blockLeft + screenX) / (blockWidth + screenX); 266 if(self.options.center){ percentageX = 0.5; percentageY = 0.5; } 267 268 // Optional individual block speed as data attr, otherwise global speed 269 var speed = (breakpoints && mapBreakpoints[currentBreakpoint] !== null) ? Number(mapBreakpoints[currentBreakpoint]) : (dataSpeed ? dataSpeed : self.options.speed); 270 var verticalSpeed = dataVerticalSpeed ? dataVerticalSpeed : self.options.verticalSpeed; 271 var horizontalSpeed = dataHorizontalSpeed ? dataHorizontalSpeed : self.options.horizontalSpeed; 272 273 // Optional individual block movement axis direction as data attr, otherwise global movement direction 274 var verticalScrollAxis = dataVericalScrollAxis ? dataVericalScrollAxis : self.options.verticalScrollAxis; 275 var horizontalScrollAxis = dataHorizontalScrollAxis ? dataHorizontalScrollAxis : self.options.horizontalScrollAxis; 276 277 var bases = updatePosition(percentageX, percentageY, speed, verticalSpeed, horizontalSpeed); 278 279 // ~~Store non-translate3d transforms~~ 280 // Store inline styles and extract transforms 281 var style = el.style.cssText; 282 var transform = ''; 283 284 // Check if there's an inline styled transform 285 var searchResult = /transform\s*:/i.exec(style); 286 if (searchResult) { 287 // Get the index of the transform 288 var index = searchResult.index; 289 290 // Trim the style to the transform point and get the following semi-colon index 291 var trimmedStyle = style.slice(index); 292 var delimiter = trimmedStyle.indexOf(';'); 293 294 // Remove "transform" string and save the attribute 295 if (delimiter) { 296 transform = " " + trimmedStyle.slice(11, delimiter).replace(/\s/g,''); 297 } else { 298 transform = " " + trimmedStyle.slice(11).replace(/\s/g,''); 299 } 300 } 301 302 return { 303 baseX: bases.x, 304 baseY: bases.y, 305 top: blockTop, 306 left: blockLeft, 307 height: blockHeight, 308 width: blockWidth, 309 speed: speed, 310 verticalSpeed: verticalSpeed, 311 horizontalSpeed: horizontalSpeed, 312 verticalScrollAxis: verticalScrollAxis, 313 horizontalScrollAxis: horizontalScrollAxis, 314 style: style, 315 transform: transform, 316 zindex: dataZindex, 317 min: dataMin, 318 max: dataMax, 319 minX: dataMinX, 320 maxX: dataMaxX, 321 minY: dataMinY, 322 maxY: dataMaxY 323 }; 324 }; 325 326 // set scroll position (posY, posX) 327 // side effect method is not ideal, but okay for now 328 // returns true if the scroll changed, false if nothing happened 329 var setPosition = function() { 330 var oldY = posY; 331 var oldX = posX; 332 333 posY = self.options.wrapper ? self.options.wrapper.scrollTop : (document.documentElement || document.body.parentNode || document.body).scrollTop || window.pageYOffset; 334 posX = self.options.wrapper ? self.options.wrapper.scrollLeft : (document.documentElement || document.body.parentNode || document.body).scrollLeft || window.pageXOffset; 335 // If option relativeToWrapper is true, use relative wrapper value instead. 336 if (self.options.relativeToWrapper) { 337 var scrollPosY = (document.documentElement || document.body.parentNode || document.body).scrollTop || window.pageYOffset; 338 posY = scrollPosY - self.options.wrapper.offsetTop; 339 } 340 341 342 if (oldY != posY && self.options.vertical) { 343 // scroll changed, return true 344 return true; 345 } 346 347 if (oldX != posX && self.options.horizontal) { 348 // scroll changed, return true 349 return true; 350 } 351 352 // scroll did not change 353 return false; 354 }; 355 356 // Ahh a pure function, gets new transform value 357 // based on scrollPosition and speed 358 // Allow for decimal pixel values 359 var updatePosition = function(percentageX, percentageY, speed, verticalSpeed, horizontalSpeed) { 360 var result = {}; 361 var valueX = ((horizontalSpeed ? horizontalSpeed : speed) * (100 * (1 - percentageX))); 362 var valueY = ((verticalSpeed ? verticalSpeed : speed) * (100 * (1 - percentageY))); 363 364 result.x = self.options.round ? Math.round(valueX) : Math.round(valueX * 100) / 100; 365 result.y = self.options.round ? Math.round(valueY) : Math.round(valueY * 100) / 100; 366 367 return result; 368 }; 369 370 // Remove event listeners and loop again 371 var deferredUpdate = function() { 372 window.removeEventListener('resize', deferredUpdate); 373 window.removeEventListener('orientationchange', deferredUpdate); 374 (self.options.wrapper ? self.options.wrapper : window).removeEventListener('scroll', deferredUpdate); 375 (self.options.wrapper ? self.options.wrapper : document).removeEventListener('touchmove', deferredUpdate); 376 377 // loop again 378 loopId = loop(update); 379 }; 380 381 // Loop 382 var update = function() { 383 if (setPosition() && pause === false) { 384 animate(); 385 386 // loop again 387 loopId = loop(update); 388 } else { 389 loopId = null; 390 391 // Don't animate until we get a position updating event 392 window.addEventListener('resize', deferredUpdate); 393 window.addEventListener('orientationchange', deferredUpdate); 394 (self.options.wrapper ? self.options.wrapper : window).addEventListener('scroll', deferredUpdate, supportsPassive ? { passive: true } : false); 395 (self.options.wrapper ? self.options.wrapper : document).addEventListener('touchmove', deferredUpdate, supportsPassive ? { passive: true } : false); 396 } 397 }; 398 399 // Transform3d on parallax element 400 var animate = function() { 401 var positions; 402 for (var i = 0; i < self.elems.length; i++){ 403 // Determine relevant movement directions 404 var verticalScrollAxis = blocks[i].verticalScrollAxis.toLowerCase(); 405 var horizontalScrollAxis = blocks[i].horizontalScrollAxis.toLowerCase(); 406 var verticalScrollX = verticalScrollAxis.indexOf("x") != -1 ? posY : 0; 407 var verticalScrollY = verticalScrollAxis.indexOf("y") != -1 ? posY : 0; 408 var horizontalScrollX = horizontalScrollAxis.indexOf("x") != -1 ? posX : 0; 409 var horizontalScrollY = horizontalScrollAxis.indexOf("y") != -1 ? posX : 0; 410 411 var percentageY = ((verticalScrollY + horizontalScrollY - blocks[i].top + screenY) / (blocks[i].height + screenY)); 412 var percentageX = ((verticalScrollX + horizontalScrollX - blocks[i].left + screenX) / (blocks[i].width + screenX)); 413 414 // Subtracting initialize value, so element stays in same spot as HTML 415 positions = updatePosition(percentageX, percentageY, blocks[i].speed, blocks[i].verticalSpeed, blocks[i].horizontalSpeed); 416 var positionY = positions.y - blocks[i].baseY; 417 var positionX = positions.x - blocks[i].baseX; 418 419 // The next two "if" blocks go like this: 420 // Check if a limit is defined (first "min", then "max"); 421 // Check if we need to change the Y or the X 422 // (Currently working only if just one of the axes is enabled) 423 // Then, check if the new position is inside the allowed limit 424 // If so, use new position. If not, set position to limit. 425 426 // Check if a min limit is defined 427 if (blocks[i].min !== null) { 428 if (self.options.vertical && !self.options.horizontal) { 429 positionY = positionY <= blocks[i].min ? blocks[i].min : positionY; 430 } 431 if (self.options.horizontal && !self.options.vertical) { 432 positionX = positionX <= blocks[i].min ? blocks[i].min : positionX; 433 } 434 } 435 436 // Check if directional min limits are defined 437 if (blocks[i].minY != null) { 438 positionY = positionY <= blocks[i].minY ? blocks[i].minY : positionY; 439 } 440 if (blocks[i].minX != null) { 441 positionX = positionX <= blocks[i].minX ? blocks[i].minX : positionX; 442 } 443 444 // Check if a max limit is defined 445 if (blocks[i].max !== null) { 446 if (self.options.vertical && !self.options.horizontal) { 447 positionY = positionY >= blocks[i].max ? blocks[i].max : positionY; 448 } 449 if (self.options.horizontal && !self.options.vertical) { 450 positionX = positionX >= blocks[i].max ? blocks[i].max : positionX; 451 } 452 } 453 454 // Check if directional max limits are defined 455 if (blocks[i].maxY != null) { 456 positionY = positionY >= blocks[i].maxY ? blocks[i].maxY : positionY; 457 } 458 if (blocks[i].maxX != null) { 459 positionX = positionX >= blocks[i].maxX ? blocks[i].maxX : positionX; 460 } 461 462 var zindex = blocks[i].zindex; 463 464 // Move that element 465 // (Set the new translation and append initial inline transforms.) 466 var translate = 'translate3d(' + (self.options.horizontal ? positionX : '0') + 'px,' + (self.options.vertical ? positionY : '0') + 'px,' + zindex + 'px) ' + blocks[i].transform; 467 self.elems[i].style[transformProp] = translate; 468 } 469 self.options.callback(positions); 470 }; 471 472 self.destroy = function() { 473 for (var i = 0; i < self.elems.length; i++){ 474 self.elems[i].style.cssText = blocks[i].style; 475 } 476 477 // Remove resize event listener if not pause, and pause 478 if (!pause) { 479 window.removeEventListener('resize', init); 480 pause = true; 481 } 482 483 // Clear the animation loop to prevent possible memory leak 484 clearLoop(loopId); 485 loopId = null; 486 }; 487 488 // Init 489 init(); 490 491 // Allow to recalculate the initial values whenever we want 492 self.refresh = init; 493 494 return self; 495 }; 496 return Rellax; 497}));
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.