1/* 2 * Parallax Support 3 * @since 5.0 added 4 */ 5(function($) 6{ 7 "use strict"; 8 9 $.avia_utilities = $.avia_utilities || {}; 10 11 $( function() 12 { 13 //activate parallax scrolling for background images (sections) and objects. 14 if( $.fn.avia_parallax ) 15 { 16 $( '.av-parallax,.av-parallax-object' ).avia_parallax(); 17 } 18 }); 19 20 /** 21 * Object Parallax scrolling feature 22 * 23 * - horizontal or vertical parallax for objects like images with responsive support 24 * - background image parallax (up to 5.0 handled by $.AviaParallaxElement) - was used for sections 25 * 26 * @since 5.0 27 * @param {} options 28 * @param {} element 29 */ 30 var AviaObjectParallaxElement = function( options, element ) 31 { 32 // do not use in old browsers that do not support this CSS 33 if( ! ( this.transform || this.transform3d ) ) 34 { 35 return; 36 } 37 38 this.options = $.extend( {}, options ); 39 this.win = $( window ); 40 this.body = $( 'body' ); 41 this.isMobile = $.avia_utilities.isMobile, // not defined in constructor 42 this.winHeight = this.win.height(); 43 this.winWidth = this.win.width(); 44 this.el = $( element ).addClass( 'active-parallax' ); 45 this.objectType = this.el.hasClass( 'av-parallax-object' ) ? 'object' : 'background-image'; 46 this.elInner = this.el; // container to use for parallax calculation - may be smaller than element e.g. due to position rules 47 this.elBackgroundParent = this.el.parent(); // parent container when 'background-image' parallax 48 this.elParallax = this.el.data( 'parallax' ) || {}; 49 this.direction = ''; 50 this.speed = 0.5; 51 this.elProperty = {}; 52 this.ticking = false, 53 this.isTransformed = false; // needed for responsive to reset transform when no parallax !!! 54 55 // set the browser transition string 56 if( $.avia_utilities.supported.transition === undefined ) 57 { 58 $.avia_utilities.supported.transition = $.avia_utilities.supports( 'transition' ); 59 } 60 61 this._init( options ); 62 }; 63 64 AviaObjectParallaxElement.prototype = 65 { 66 mediaQueries: { 67 'av-mini-': '(max-width: 479px)', 68 'av-small-': '(min-width: 480px) and (max-width: 767px)', 69 'av-medium-': '(min-width: 768px) and (max-width: 989px)', 70 'av-desktop-': '(min-width: 990px)' 71 }, 72 73 transform: document.documentElement.className.indexOf( 'avia_transform' ) !== -1, 74 transform3d: document.documentElement.className.indexOf( 'avia_transform3d' ) !== -1, 75 mobileNoAnimation: $( 'body' ).hasClass( 'avia-mobile-no-animations' ), 76 defaultSpeed: 0.5, 77 defaultDirections: [ 'bottom_top', 'left_right', 'right_left', 'no_parallax' ], 78 transformCSSProps: [ 'transform', '-webkit-transform', '-moz-transform', '-ms-transform', '-o-transform' ], 79 matrixDef: [ 1, 0, 0, 1, 0, 0 ], 80 matrix3dDef: [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ], 81 82 _init: function() 83 { 84 var _self = this; 85 86 // select inner container if necessary for positioning 87 if( typeof this.el.data( 'parallax-selector') != 'undefined' && this.el.data( 'parallax-selector') !== '' ) 88 { 89 this.elInner = this.el.find( this.el.data( 'parallax-selector' ) ); 90 if( this.elInner.length == 0 ) 91 { 92 this.elInner = this.el; 93 } 94 } 95 96 if( 'background-image' == this.objectType ) 97 { 98 // shortcut and exit 99 if( this.isMobile && this.mobileNoAnimation ) 100 { 101 return; 102 } 103 104 // by default we slow down 105 this.elParallax.parallax = 'bottom_top'; 106 this.elParallax.parallax_speed = parseFloat( this.el.data( 'avia-parallax-ratio' ) ) || 0.5; 107 } 108 109 //fetch window constants 110 setTimeout( function() 111 { 112 _self._fetchProperties(); 113 }, 30 ); 114 115 this.win.on( 'debouncedresize av-height-change', _self._fetchProperties.bind( _self ) ); 116 this.body.on( 'av_resize_finished', _self._fetchProperties.bind( _self ) ); 117 118 //activate the scrolling 119 setTimeout( function() 120 { 121 _self.win.on( 'scroll', _self._onScroll.bind( _self ) ); 122 }, 100 ); 123 }, 124 125 _setParallaxProps: function() 126 { 127 if( 'background-image' == this.objectType ) 128 { 129 this.direction = this.elParallax.parallax; 130 this.speed = this.elParallax.parallax_speed; 131 return; 132 } 133 134 var all_direction = this.elParallax.parallax || '', 135 all_speed = this.elParallax.parallax_speed || '', 136 resp_direction = '', 137 resp_speed = '', 138 media = 'all'; 139 140 if( this.defaultDirections.indexOf( all_direction ) < 0 ) 141 {
142 all_direction = 'no_parallax'; 143 } 144 145 if( typeof window.matchMedia == 'function' ) 146 { 147 $.each( this.mediaQueries, function( key, query ) 148 { 149 var mql = window.matchMedia( query ); 150 if( mql.matches ) 151 { 152 media = key; 153 return false; 154 } 155 }); 156 } 157 158 if( 'all' == media ) 159 { 160 this.direction = all_direction; 161 this.speed = '' == all_speed ? this.defaultSpeed : parseFloat( all_speed ) / 100.0; 162 return; 163 } 164 165 resp_direction = this.elParallax[ media + 'parallax' ] || ''; 166 resp_speed = this.elParallax[ media + 'parallax_speed' ] || ''; 167 168 if( 'inherit' == resp_direction ) 169 { 170 resp_direction = all_direction; 171 resp_speed = all_speed; 172 } 173 174 if( this.defaultDirections.indexOf( resp_direction ) < 0 ) 175 { 176 resp_direction = 'no_parallax'; 177 } 178 179 this.direction = resp_direction; 180 this.speed = '' == resp_speed ? this.defaultSpeed : parseFloat( resp_speed ) / 100.0; 181 }, 182 183 _getTranslateObject: function( element ) 184 { 185 // https://zellwk.com/blog/css-translate-values-in-javascript/ 186 // This function might not work properly if stacked transform operations are used - this is a limitation 187 var translate = { 188 type: '', 189 matrix: [], 190 x: 0, 191 y: 0, 192 z: 0 193 }; 194 195 $.each( this.transformCSSProps, function( i, prop ) 196 { 197 var found = element.css( prop ); 198 199 if( 'string' != typeof found || 'none' == found ) 200 { 201 return; 202 } 203 204 if( found.indexOf( 'matrix' ) >= 0 ) 205 { 206 var matrixValues = found.match( /matrix.*\((.+)\)/)[1].split( ', ' ); 207 208 if( found.indexOf( 'matrix3d' ) >= 0 ) 209 { 210 translate.type = '3d'; 211 translate.matrix = matrixValues; 212 213 // 3d have 16 values 214 translate.x = matrixValues[12]; 215 translate.y = matrixValues[13]; 216 translate.z = matrixValues[14]; 217 } 218 else 219 { 220 translate.type = '2d'; 221 translate.matrix = matrixValues; 222 223 // 2d have 6 values 224 translate.x = matrixValues[4]; 225 translate.y = matrixValues[5]; 226 } 227 228 return false; 229 } 230 else 231 { 232 translate.type = ''; 233 234 // translateX 235 var matchX = found.match( /translateX\((-?\d+\.?\d*px)\)/ ); 236 if( matchX ) 237 { 238 translate.x = parseInt( matchX[1], 10 ); 239 } 240 241 // translateY 242 var matchY = found.match( /translateY\((-?\d+\.?\d*px)\)/ ); 243 if( matchY ) 244 { 245 translate.y = parseInt( matchY[1], 10 ); 246 } 247 } 248 }); 249 250 return translate; 251 }, 252 253 _getTranslateMatrix: function( translateObj, changes ) 254 { 255 // matrix( a, b, c, d, tx, ty ) 256 // matrix3d( a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1 ) 257 var matrix = ''; 258 259 $.each( changes, function( key, value ) 260 { 261 translateObj[key] = value; 262 }); 263 264 if( this.transform3d ) 265 { 266 var matrix3d = this.matrix3dDef.slice( 0 ); 267 268 switch( translateObj.type ) 269 { 270 case '2d': 271 // 2d have 6 values 272 matrix3d[0] = translateObj.matrix[0]; 273 matrix3d[1] = translateObj.matrix[1]; 274 matrix3d[4] = translateObj.matrix[2]; 275 matrix3d[5] = translateObj.matrix[3]; 276 matrix3d[12] = translateObj.x; 277 matrix3d[13] = translateObj.y; 278 break; 279 case '3d': 280 // 3d have 16 values 281 matrix3d = translateObj.matrix.slice( 0 ); 282 matrix3d[12] = translateObj.x; 283 matrix3d[13] = translateObj.y; 284 matrix3d[14] = translateObj.z; 285 break; 286 default: 287 matrix3d[12] = translateObj.x; 288 matrix3d[13] = translateObj.y; 289 break; 290 } 291 292 matrix = 'matrix3d(' + matrix3d.join( ', ' ) + ')'; 293 } 294 else if( this.transform ) 295 { 296 var matrix2d = this.matrixDef.slice( 0 ); 297 298 switch( translateObj.type ) 299 { 300 case '2d': 301 // 2d have 6 values 302 matrix2d = translateObj.matrix.slice( 0 ); 303 matrix2d[4] = translateObj.x; 304 matrix2d[5] = translateObj.y; 305 break; 306 case '3d': // fallback only 307 // 3d have 16 values 308 matrix2d[0] = translateObj.matrix[0]; 309 matrix2d[1] = translateObj.matrix[1]; 310 matrix2d[2] = translateObj.matrix[4]; 311 matrix2d[3] = translateObj.matrix[5];
312 matrix2d[4] = translateObj.x; 313 matrix2d[5] = translateObj.y; 314 break; 315 default: 316 matrix2d[4] = translateObj.x; 317 matrix2d[5] = translateObj.y; 318 break; 319 } 320 321 matrix = 'matrix(' + matrix2d.join( ', ' ) + ')'; 322 } 323 324 return matrix; 325 }, 326 327 _fetchProperties: function() 328 { 329 this._setParallaxProps(); 330 331 // unset any added transform styles to get real CSS position before apply parallax transforms again 332 this.el.css( $.avia_utilities.supported.transition + 'transform', '' ); 333 334 // cache values that only change on resize of viewport 335 this.winHeight = this.win.height(); 336 this.winWidth = this.win.width(); 337 338 if( 'background-image' == this.objectType ) 339 { 340 // special case where we have a div with background image 341 this.elProperty.top = this.elBackgroundParent.offset().top; 342 this.elProperty.height = this.elBackgroundParent.outerHeight(); 343 344 // set the height of the element based on the windows height, offset ratio and parent height 345 this.el.height( Math.ceil( ( this.winHeight * Math.abs( this.speed ) ) + this.elProperty.height ) ); 346 } 347 else 348 { 349 this.elProperty.top = this.elInner.offset().top; 350 this.elProperty.left = this.elInner.offset().left; 351 this.elProperty.height = this.elInner.outerHeight(); 352 this.elProperty.width = this.elInner.outerWidth(); 353 this.elProperty.bottom = this.elProperty.top + this.elProperty.height; 354 this.elProperty.right = this.elProperty.left + this.elProperty.width; 355 356 this.elProperty.distanceLeft = this.elProperty.right; 357 this.elProperty.distanceRight = this.winWidth - this.elProperty.left; 358 } 359 360 // Save original position of element relative to container 361 this.elProperty.translateObj = this._getTranslateObject( this.el ); 362 363 //re-position the element 364 this._parallaxScroll(); 365 }, 366 367 _onScroll: function( e ) 368 { 369 var _self = this; 370 371 if( ! _self.ticking ) 372 { 373 _self.ticking = true; 374 window.requestAnimationFrame( _self._parallaxRequest.bind( _self ) ); 375 } 376 }, 377 378 _inViewport: function( elTop, elRight, elBottom, elLeft, winTop, winBottom, winLeft, winRight ) 379 { 380 // add a few pixel to be on safe side 381 return ! ( elTop > winBottom + 10 || elBottom < winTop - 10 || elLeft > winRight + 10 || elRight < winLeft - 10 ); 382 }, 383 384 _parallaxRequest: function( e ) 385 { 386 // https://stackoverflow.com/questions/47184298/why-is-it-recommend-to-nest-settimeout-in-requestanimationframe-when-scheduling 387 var _self = this; 388 setTimeout( _self._parallaxScroll.bind( _self ), 0 ); 389 }, 390 391 _parallaxScroll: function( e ) 392 { 393 // shortcut 394 if( ( 'no_parallax' == this.direction || '' == this.direction ) && ! this.isTransformed ) 395 { 396 this.ticking = false; 397 return; 398 } 399 400 var winTop = this.win.scrollTop(), 401 winLeft = this.win.scrollLeft(), 402 winRight = winLeft + this.winWidth, 403 winBottom = winTop + this.winHeight, 404 scrollPos = 0, 405 matrix = ''; 406 407 // special case where we have a div with background image - 'bottom_top' 408 if( 'background-image' == this.objectType ) 409 { 410 // shift element when it moves into viewport 411 if( this.elProperty.top < winBottom && winTop <= this.elProperty.top + this.elProperty.height ) 412 { 413 scrollPos = Math.ceil( ( winBottom - this.elProperty.top ) * this.speed ); 414 matrix = this._getTranslateMatrix( this.elProperty.translateObj, { y: scrollPos } ); 415 416 this.el.css( $.avia_utilities.supported.transition + 'transform', matrix ); 417 } 418 419 this.ticking = false; 420 return; 421 } 422 423 // reset and shortcut 424 if( ( 'no_parallax' == this.direction || '' == this.direction ) ) 425 { 426 matrix = this._getTranslateMatrix( this.elProperty.translateObj, { x: 0, y: 0 } ); 427 this.el.css( $.avia_utilities.supported.transition + 'transform', matrix ); 428 429 this.ticking = false; 430 this.isTransformed = false; 431 return; 432 } 433 434 // Get current coordinates for element 435 var scroll_px_toTop = Math.ceil( this.elProperty.top - winTop ), 436 scroll_px_el = Math.ceil( winBottom - this.elProperty.top ), 437 scrolled_pc_toTop = 0, 438 reduceDistanceX = 0, 439 transform = { x: 0, y: 0 }; 440 441 // if element is initially in viewport on unscrolled screen we leave it and reduce distance to move 442 if( this.elProperty.top < this.winHeight ) 443 { 444 reduceDistanceX = Math.ceil( this.winHeight - this.elProperty.top ); 445 } 446 447 // Calculate transform value 448 if( this.elProperty.top > winBottom ) 449 { 450 // element below viewport 451 scrolled_pc_toTop = 0; 452 scroll_px_el = 0; 453 } 454 else 455 {
456 // container is inside viewport or above ( scroll_px_toTop is negative) 457 scrolled_pc_toTop = 1 - ( scroll_px_toTop + reduceDistanceX ) / this.winHeight; 458 } 459 460 switch( this.direction ) 461 { 462 case 'bottom_top': 463 scrollPos = Math.ceil( ( scroll_px_el - reduceDistanceX ) * this.speed ); 464 transform.y = -scrollPos; 465 matrix = this._getTranslateMatrix( this.elProperty.translateObj, { y: -scrollPos } ); 466 break; 467 case 'left_right': 468 scrollPos = Math.ceil( this.elProperty.distanceRight * scrolled_pc_toTop * this.speed ); 469 transform.x = scrollPos; 470 matrix = this._getTranslateMatrix( this.elProperty.translateObj, { x: scrollPos } ); 471 break; 472 case 'right_left': 473 scrollPos = Math.ceil( this.elProperty.distanceLeft * scrolled_pc_toTop * this.speed ); 474 transform.x = -scrollPos; 475 matrix = this._getTranslateMatrix( this.elProperty.translateObj, { x: -scrollPos } ); 476 break; 477 default: 478 break; 479 } 480 481 var elInViewport = this._inViewport( this.elProperty.top, this.elProperty.right, this.elProperty.bottom, this.elProperty.left, winTop, winBottom, winLeft, winRight ), 482 transformedInViewport = this._inViewport( this.elProperty.top + transform.y, this.elProperty.right + transform.x, this.elProperty.bottom + transform.y, this.elProperty.left + transform.x, winTop, winBottom, winLeft, winRight ); 483 484 if( elInViewport || transformedInViewport ) 485 { 486 this.el.css( $.avia_utilities.supported.transition + 'transform', matrix ); 487 } 488 489 this.ticking = false; 490 this.isTransformed = true; 491 } 492 }; 493 494 /** 495 * Wrapper to avoid double initialization of object 496 * 497 * @param {} options 498 */ 499 $.fn.avia_parallax = function( options ) 500 { 501 502// if( window.location.search.includes('new-parallax') ) //for testing 2 versions in 2 browser windows 503// { 504// return this; 505// } 506 507 return this.each( function() 508 { 509 var obj = $( this ); 510 var self = obj.data( 'aviaParallax' ); 511 512 if( ! self ) 513 { 514 self = obj.data( 'aviaParallax', new AviaObjectParallaxElement( options, this ) ); 515 } 516 }); 517 }; 518 519})( 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.