1/*! 2 * Name : Just Another Parallax [Jarallax] 3 * Version : 1.8.0 4 * Author : _nK https://nkdev.info 5 * GitHub : https://github.com/nk-o/jarallax 6 */ 7(function (window) { 8 'use strict'; 9 10 // Adapted from https://gist.github.com/paulirish/1579671 11 if(!Date.now) { 12 Date.now = function () { return new Date().getTime(); }; 13 } 14 if(!window.requestAnimationFrame) { 15 (function () { 16 17 var vendors = ['webkit', 'moz']; 18 for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) { 19 var vp = vendors[i]; 20 window.requestAnimationFrame = window[vp+'RequestAnimationFrame']; 21 window.cancelAnimationFrame = window[vp+'CancelAnimationFrame'] 22 || window[vp+'CancelRequestAnimationFrame']; 23 } 24 if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy 25 || !window.requestAnimationFrame || !window.cancelAnimationFrame) { 26 var lastTime = 0; 27 window.requestAnimationFrame = function (callback) { 28 var now = Date.now(); 29 var nextTime = Math.max(lastTime + 16, now); 30 return setTimeout(function () { callback(lastTime = nextTime); }, 31 nextTime - now); 32 }; 33 window.cancelAnimationFrame = clearTimeout; 34 } 35 }()); 36 } 37 38 // test if css property supported by browser 39 // like "transform" 40 var tempDiv = document.createElement('div'); 41 function isPropertySupported (property) { 42 var prefixes = ['O','Moz','ms','Ms','Webkit']; 43 var i = prefixes.length; 44 if (tempDiv.style[property] !== undefined) { 45 return true; 46 } 47 property = property.charAt(0).toUpperCase() + property.substr(1); 48 while (--i > -1 && tempDiv.style[prefixes[i] + property] === undefined) { } 49 return i >= 0; 50 } 51 52 var supportTransform = isPropertySupported('transform'); 53 var supportTransform3D = isPropertySupported('perspective'); 54 55 var ua = navigator.userAgent; 56 var isAndroid = ua.toLowerCase().indexOf('android') > -1; 57 var isIOs = /iPad|iPhone|iPod/.test(ua) && !window.MSStream; 58 var isFirefox = ua.toLowerCase().indexOf('firefox') > -1; 59 var isIE = ua.indexOf('MSIE ') > -1 // IE 10 or older 60 || ua.indexOf('Trident/') > -1 // IE 11 61 || ua.indexOf('Edge/') > -1; // Edge 62 var isIElt10 = document.all && !window.atob; // IE 9 or older 63 64 var wndW; 65 var wndH; 66 function updateWndVars () { 67 wndW = window.innerWidth || document.documentElement.clientWidth; 68 wndH = window.innerHeight || document.documentElement.clientHeight; 69 } 70 updateWndVars(); 71 72 // list with all jarallax instances 73 // need to render all in one scroll/resize event 74 var jarallaxList = []; 75 76 // Jarallax instance 77 var Jarallax = (function () { 78 var instanceID = 0; 79 80 function Jarallax_inner (item, userOptions) { 81 var _this = this, 82 dataOptions; 83 84 _this.$item = item; 85 86 _this.defaults = { 87 type : 'scroll', // type of parallax: scroll, scale, opacity, scale-opacity, scroll-opacity 88 speed : 0.5, // supported value from -1 to 2 89 imgSrc : null, 90 imgWidth : null, 91 imgHeight : null, 92 elementInViewport : null, 93 zIndex : -100, 94 noAndroid : false, 95 noIos : true, 96 97 // events 98 onScroll : null, // function(calculations) {} 99 onInit : null, // function() {} 100 onDestroy : null, // function() {} 101 onCoverImage : null // function() {} 102 }; 103 dataOptions = JSON.parse(_this.$item.getAttribute('data-jarallax') || '{}'); 104 _this.options = _this.extend({}, _this.defaults, dataOptions, userOptions); 105 106 // stop init if android or ios 107 if(!supportTransform || isAndroid && _this.options.noAndroid || isIOs && _this.options.noIos) { 108 return; 109 } 110 111 // fix speed option [-1.0, 2.0] 112 _this.options.speed = Math.min(2, Math.max(-1, parseFloat(_this.options.speed))); 113 114 // custom element to check if parallax in viewport 115 var elementInVP = _this.options.elementInViewport; 116 // get first item from array 117 if(elementInVP && typeof elementInVP === 'object' && typeof elementInVP.length !== 'undefined') { 118 elementInVP = elementInVP[0]; 119 } 120 // check if dom element 121 if(!elementInVP instanceof Element) { 122 elementInVP = null; 123 } 124 _this.options.elementInViewport = elementInVP; 125 126 _this.instanceID = instanceID++; 127 128 _this.image = { 129 src : _this.options.imgSrc || null, 130 $container : null, 131 $item : null, 132 width : _this.options.imgWidth || null, 133 height : _this.options.imgHeight || null, 134 // fix for some devices 135 // use <img> instead of background image - more smoothly
136 useImgTag : isIOs || isAndroid || isIE, 137 138 // position absolute is needed on IE9 and FireFox because fixed position have glitches 139 position : !supportTransform3D || isFirefox ? 'absolute' : 'fixed' 140 }; 141 142 if(_this.initImg()) { 143 _this.init(); 144 } 145 } 146 147 return Jarallax_inner; 148 }()); 149 150 // add styles to element 151 Jarallax.prototype.css = function (el, styles) { 152 if(typeof styles === 'string') { 153 if(window.getComputedStyle) { 154 return window.getComputedStyle(el).getPropertyValue(styles); 155 } 156 return el.style[styles]; 157 } 158 159 // add transform property with vendor prefixes 160 if(styles.transform) { 161 if (supportTransform3D) { 162 styles.transform += ' translateZ(0)'; 163 } 164 styles.WebkitTransform = styles.MozTransform = styles.msTransform = styles.OTransform = styles.transform; 165 } 166 167 for(var k in styles) { 168 el.style[k] = styles[k]; 169 } 170 return el; 171 }; 172 // Extend like jQuery.extend 173 Jarallax.prototype.extend = function (out) { 174 out = out || {}; 175 for (var i = 1; i < arguments.length; i++) { 176 if (!arguments[i]) { 177 continue; 178 } 179 for (var key in arguments[i]) { 180 if (arguments[i].hasOwnProperty(key)) { 181 out[key] = arguments[i][key]; 182 } 183 } 184 } 185 return out; 186 }; 187 188 // Jarallax functions 189 Jarallax.prototype.initImg = function () { 190 var _this = this; 191 192 // get image src 193 if(_this.image.src === null) { 194 _this.image.src = _this.css(_this.$item, 'background-image').replace(/^url\(['"]?/g,'').replace(/['"]?\)$/g,''); 195 } 196 return !(!_this.image.src || _this.image.src === 'none'); 197 }; 198 199 Jarallax.prototype.init = function () { 200 var _this = this, 201 containerStyles = { 202 position : 'absolute', 203 top : 0, 204 left : 0, 205 width : '100%', 206 height : '100%', 207 overflow : 'hidden', 208 pointerEvents : 'none' 209 }, 210 imageStyles = {}; 211 212 // save default user styles 213 _this.$item.setAttribute('data-jarallax-original-styles', _this.$item.getAttribute('style')); 214 215 // set relative position and z-index to the parent 216 if (_this.css(_this.$item, 'position') === 'static') { 217 _this.css(_this.$item, { 218 position: 'relative' 219 }); 220 } 221 if (_this.css(_this.$item, 'z-index') === 'auto') { 222 _this.css(_this.$item, { 223 zIndex: 0 224 }); 225 } 226 227 // container for parallax image 228 _this.image.$container = document.createElement('div'); 229 _this.css(_this.image.$container, containerStyles); 230 _this.css(_this.image.$container, { 231 visibility : 'hidden', 232 'z-index' : _this.options.zIndex 233 }); 234 _this.image.$container.setAttribute('id', 'jarallax-container-' + _this.instanceID); 235 _this.$item.appendChild(_this.image.$container); 236 237 // use img tag 238 if(_this.image.useImgTag) { 239 _this.image.$item = document.createElement('img'); 240 _this.image.$item.setAttribute('src', _this.image.src); 241 imageStyles = _this.extend({ 242 'max-width' : 'none' 243 }, containerStyles, imageStyles); 244 } 245 246 // use div with background image 247 else { 248 _this.image.$item = document.createElement('div'); 249 imageStyles = _this.extend({ 250 'background-position' : '50% 50%', 251 'background-size' : '100% auto', 252 'background-repeat' : 'no-repeat no-repeat', 253 'background-image' : 'url("' + _this.image.src + '")' 254 }, containerStyles, imageStyles); 255 } 256 257 // check if one of parents have transform style (without this check, scroll transform will be inverted) 258 // discussion - https://github.com/nk-o/jarallax/issues/9 259 var parentWithTransform = 0; 260 var $itemParents = _this.$item; 261 while ($itemParents !== null && $itemParents !== document && parentWithTransform === 0) { 262 var parent_transform = _this.css($itemParents, '-webkit-transform') || _this.css($itemParents, '-moz-transform') || _this.css($itemParents, 'transform'); 263 if(parent_transform && parent_transform !== 'none') { 264 parentWithTransform = 1; 265 266 // add transform on parallax container if there is parent with transform 267 _this.css(_this.image.$container, { 268 transform: 'translateX(0) translateY(0)' 269 }); 270 } 271 $itemParents = $itemParents.parentNode; 272 } 273 274 // absolute position if one of parents have transformations or parallax without scroll 275 if (parentWithTransform || _this.options.type === 'opacity'|| _this.options.type === 'scale' || _this.options.type === 'scale-opacity') { 276 _this.image.position = 'absolute'; 277 } 278 279 // add position to parallax block 280 imageStyles.position = _this.image.position; 281 282 // parallax image 283 _this.css(_this.image.$item, imageStyles); 284 _this.image.$container.appendChild(_this.image.$item); 285 286 // cover image if width and height is ready 287 function initAfterReady () { 288 _this.coverImage(); 289 _this.clipContainer(); 290 _this.onScroll(true); 291 292 // call onInit event 293 if(_this.options.onInit) { 294 _this.options.onInit.call(_this); 295 } 296 297 // timeout to fix IE blinking 298 setTimeout(function () { 299 if(_this.$item) { 300 // remove default user background 301 _this.css(_this.$item, { 302 'background-image' : 'none', 303 'background-attachment' : 'scroll', 304 'background-size' : 'auto' 305 }); 306 } 307 }, 0); 308 } 309 310 if(_this.image.width && _this.image.height) { 311 // init if width and height already exists 312 initAfterReady(); 313 } else { 314 // load image and get width and height 315 _this.getImageSize(_this.image.src, function (width, height) { 316 _this.image.width = width; 317 _this.image.height = height; 318 initAfterReady(); 319 }); 320 } 321 322 jarallaxList.push(_this); 323 }; 324 325 Jarallax.prototype.destroy = function () { 326 var _this = this; 327 328 // remove from instances list 329 for(var k = 0, len = jarallaxList.length; k < len; k++) { 330 if(jarallaxList[k].instanceID === _this.instanceID) { 331 jarallaxList.splice(k, 1); 332 break; 333 } 334 } 335 336 // return styles on container as before jarallax init 337 var originalStylesTag = _this.$item.getAttribute('data-jarallax-original-styles'); 338 _this.$item.removeAttribute('data-jarallax-original-styles'); 339 // null occurs if there is no style tag before jarallax init 340 if(originalStylesTag === 'null') { 341 _this.$item.removeAttribute('style'); 342 } else { 343 _this.$item.setAttribute('style', originalStylesTag); 344 } 345 346 // remove additional dom elements 347 if(_this.$clipStyles) { 348 _this.$clipStyles.parentNode.removeChild(_this.$clipStyles); 349 } 350 _this.image.$container.parentNode.removeChild(_this.image.$container); 351 352 // call onDestroy event 353 if(_this.options.onDestroy) { 354 _this.options.onDestroy.call(_this); 355 } 356 357 // delete jarallax from item 358 delete _this.$item.jarallax; 359 360 // delete all variables 361 for(var n in _this) { 362 delete _this[n]; 363 } 364 }; 365 366 Jarallax.prototype.getImageSize = function (src, callback) { 367 if(!src || !callback) { 368 return; 369 } 370 371 var tempImg = new Image(); 372 tempImg.onload = function () { 373 callback(tempImg.width, tempImg.height); 374 }; 375 tempImg.src = src; 376 }; 377 378 // it will remove some image overlapping 379 // overlapping occur due to an image position fixed inside absolute position element (webkit based browsers works without any fix) 380 Jarallax.prototype.clipContainer = function () { 381 // clip is not working properly on real IE9 and less 382 if(isIElt10) { 383 return; 384 } 385 386 var _this = this, 387 rect = _this.image.$container.getBoundingClientRect(), 388 width = rect.width, 389 height = rect.height; 390 391 if(!_this.$clipStyles) { 392 _this.$clipStyles = document.createElement('style'); 393 _this.$clipStyles.setAttribute('type', 'text/css'); 394 _this.$clipStyles.setAttribute('id', '#jarallax-clip-' + _this.instanceID); 395 var head = document.head || document.getElementsByTagName('head')[0]; 396 head.appendChild(_this.$clipStyles); 397 } 398 399 var styles = [ 400 '#jarallax-container-' + _this.instanceID + ' {', 401 ' clip: rect(0 ' + width + 'px ' + height + 'px 0);', 402 ' clip: rect(0, ' + width + 'px, ' + height + 'px, 0);', 403 '}' 404 ].join('\n'); 405 406 // add clip styles inline (this method need for support IE8 and less browsers) 407 if (_this.$clipStyles.styleSheet){ 408 _this.$clipStyles.styleSheet.cssText = styles; 409 } else { 410 _this.$clipStyles.innerHTML = styles; 411 } 412 }; 413 414 Jarallax.prototype.coverImage = function () { 415 var _this = this; 416 417 if(!_this.image.width || !_this.image.height) { 418 return; 419 } 420 421 var rect = _this.image.$container.getBoundingClientRect(), 422 contW = rect.width, 423 contH = rect.height, 424 contL = rect.left, 425 imgW = _this.image.width, 426 imgH = _this.image.height, 427 speed = _this.options.speed, 428 isScroll = _this.options.type === 'scroll' || _this.options.type === 'scroll-opacity', 429 scrollDist = 0, 430 resultW = 0, 431 resultH = contH, 432 resultML = 0, 433 resultMT = 0; 434 435 // scroll parallax 436 if(isScroll) { 437 // scroll distance and height for image 438 if (speed < 0) { 439 scrollDist = speed * Math.max(contH, wndH); 440 } else { 441 scrollDist = speed * (contH + wndH); 442 } 443 444 // size for scroll parallax 445 if (speed > 1) { 446 resultH = Math.abs(scrollDist - wndH); 447 } else if (speed < 0) { 448 resultH = scrollDist / speed + Math.abs(scrollDist); 449 } else { 450 resultH += Math.abs(wndH - contH) * (1 - speed); 451 } 452 453 scrollDist /= 2; 454 } 455 456 // calculate width relative to height and image size 457 resultW = resultH * imgW / imgH; 458 if(resultW < contW) { 459 resultW = contW; 460 resultH = resultW * imgH / imgW; 461 } 462 463 // center parallax image 464 if(isScroll) { 465 resultML = contL + (contW - resultW) / 2; 466 resultMT = (wndH - resultH) / 2; 467 } else { 468 resultML = (contW - resultW) / 2; 469 resultMT = (contH - resultH) / 2; 470 } 471
472 // fix if parallax block in absolute position 473 if(_this.image.position === 'absolute') { 474 resultML -= contL; 475 } 476 477 // store scroll distance 478 _this.parallaxScrollDistance = scrollDist; 479 480 // apply result to item 481 _this.css(_this.image.$item, { 482 width: resultW + 'px', 483 height: resultH + 'px', 484 marginLeft: resultML + 'px', 485 marginTop: resultMT + 'px' 486 }); 487 488 // call onCoverImage event 489 if(_this.options.onCoverImage) { 490 _this.options.onCoverImage.call(_this); 491 } 492 }; 493 494 Jarallax.prototype.isVisible = function () { 495 return this.isElementInViewport || false; 496 }; 497 498 Jarallax.prototype.onScroll = function (force) { 499 var _this = this; 500 501 if(!_this.image.width || !_this.image.height) { 502 return; 503 } 504 505 var rect = _this.$item.getBoundingClientRect(), 506 contT = rect.top, 507 contH = rect.height, 508 styles = { 509 visibility : 'visible', 510 backgroundPosition : '50% 50%' 511 }; 512 513 // check if in viewport 514 var viewportRect = rect; 515 if(_this.options.elementInViewport) { 516 viewportRect = _this.options.elementInViewport.getBoundingClientRect(); 517 } 518 _this.isElementInViewport = 519 viewportRect.bottom >= 0 && 520 viewportRect.right >= 0 && 521 viewportRect.top <= wndH && 522 viewportRect.left <= wndW; 523 524 // stop calculations if item is not in viewport 525 if (force ? false : !_this.isElementInViewport) { 526 return; 527 } 528 529 // calculate parallax helping variables 530 var beforeTop = Math.max(0, contT), 531 beforeTopEnd = Math.max(0, contH + contT), 532 afterTop = Math.max(0, -contT), 533 beforeBottom = Math.max(0, contT + contH - wndH), 534 beforeBottomEnd = Math.max(0, contH - (contT + contH - wndH)), 535 afterBottom = Math.max(0, -contT + wndH - contH), 536 fromViewportCenter = 1 - 2 * (wndH - contT) / (wndH + contH); 537 538 // calculate on how percent of section is visible 539 var visiblePercent = 1; 540 if(contH < wndH) { 541 visiblePercent = 1 - (afterTop || beforeBottom) / contH; 542 } else { 543 if(beforeTopEnd <= wndH) { 544 visiblePercent = beforeTopEnd / wndH; 545 } else if (beforeBottomEnd <= wndH) { 546 visiblePercent = beforeBottomEnd / wndH; 547 } 548 } 549 550 // opacity 551 if(_this.options.type === 'opacity' || _this.options.type === 'scale-opacity' || _this.options.type === 'scroll-opacity') { 552 styles.transform = ''; // empty to add translateZ(0) where it is possible 553 styles.opacity = visiblePercent; 554 } 555 556 // scale 557 if(_this.options.type === 'scale' || _this.options.type === 'scale-opacity') { 558 var scale = 1; 559 if(_this.options.speed < 0) { 560 scale -= _this.options.speed * visiblePercent; 561 } else { 562 scale += _this.options.speed * (1 - visiblePercent); 563 } 564 styles.transform = 'scale(' + scale + ')'; 565 } 566 567 // scroll 568 if(_this.options.type === 'scroll' || _this.options.type === 'scroll-opacity') { 569 var positionY = _this.parallaxScrollDistance * fromViewportCenter; 570 571 // fix if parallax block in absolute position 572 if(_this.image.position === 'absolute') { 573 positionY -= contT; 574 } 575 576 styles.transform = 'translateY(' + positionY + 'px)'; 577 } 578 579 _this.css(_this.image.$item, styles); 580 581 // call onScroll event 582 if(_this.options.onScroll) { 583 _this.options.onScroll.call(_this, { 584 section: rect, 585 586 beforeTop: beforeTop, 587 beforeTopEnd: beforeTopEnd, 588 afterTop: afterTop, 589 beforeBottom: beforeBottom, 590 beforeBottomEnd: beforeBottomEnd, 591 afterBottom: afterBottom, 592 593 visiblePercent: visiblePercent, 594 fromViewportCenter: fromViewportCenter 595 }); 596 } 597 }; 598 599 600 // init events 601 function addEventListener (el, eventName, handler) { 602 if (el.addEventListener) { 603 el.addEventListener(eventName, handler); 604 } else { 605 el.attachEvent('on' + eventName, function (){ 606 handler.call(el); 607 }); 608 } 609 } 610 611 function update (e) { 612 window.requestAnimationFrame(function () { 613 if(e.type !== 'scroll') { 614 updateWndVars(); 615 } 616 for(var k = 0, len = jarallaxList.length; k < len; k++) { 617 // cover image and clip needed only when parallax container was changed 618 if(e.type !== 'scroll') { 619 jarallaxList[k].coverImage(); 620 jarallaxList[k].clipContainer(); 621 } 622 jarallaxList[k].onScroll(); 623 } 624 }); 625 } 626 addEventListener(window, 'scroll', update); 627 addEventListener(window, 'resize', update); 628 addEventListener(window, 'orientationchange', update); 629 addEventListener(window, 'load', update); 630 631 632 // global definition 633 var plugin = function (items) { 634 // check for dom element 635 // thanks: http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object 636 if(typeof HTMLElement === "object" ? items instanceof HTMLElement : items && typeof items === "object" && items !== null && items.nodeType === 1 && typeof items.nodeName==="string") { 637 items = [items]; 638 } 639 640 var options = arguments[1], 641 args = Array.prototype.slice.call(arguments, 2), 642 len = items.length, 643 k = 0, 644 ret; 645 646 for (k; k < len; k++) { 647 if (typeof options === 'object' || typeof options === 'undefined') { 648 if(!items[k].jarallax) { 649 items[k].jarallax = new Jarallax(items[k], options); 650 } 651 } 652 else if(items[k].jarallax) { 653 ret = items[k].jarallax[options].apply(items[k].jarallax, args); 654 } 655 if (typeof ret !== 'undefined') { 656 return ret; 657 } 658 } 659 660 return items; 661 }; 662 plugin.constructor = Jarallax; 663 664 // no conflict 665 var oldPlugin = window.jarallax; 666 window.jarallax = plugin; 667 window.jarallax.noConflict = function () { 668 window.jarallax = oldPlugin; 669 return this; 670 }; 671 672 // jQuery support 673 if(typeof jQuery !== 'undefined') { 674 var jQueryPlugin = function () { 675 var args = arguments || []; 676 Array.prototype.unshift.call(args, this); 677 var res = plugin.apply(window, args); 678 return typeof res !== 'object' ? res : this; 679 }; 680 jQueryPlugin.constructor = Jarallax; 681 682 // no conflict 683 var oldJqPlugin = jQuery.fn.jarallax; 684 jQuery.fn.jarallax = jQueryPlugin; 685 jQuery.fn.jarallax.noConflict = function () { 686 jQuery.fn.jarallax = oldJqPlugin; 687 return this; 688 }; 689 } 690 691 // data-jarallax initialization 692 addEventListener(window, 'DOMContentLoaded', function () { 693 plugin(document.querySelectorAll('[data-jarallax], [data-jarallax-video]')); 694 }); 695}(window));
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.