PageSourceSearch

https://ecma-international.org/wp-content/themes/enfold/js/avia-snippet-parallax.js?ver=8.1

js ecma-international.org collected 2026-09-24 07:18:19 UTC 15,014 bytes, 519 lines download raw bytes

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.