1 2/** 3 * SmoothParallax 1.1.2 4 * 5 * File smooth-parallax.js. 6 * 7 * Yet another parallax script. Smooth parallax is intended to make it a lot easier to 8 * make objects move vertically or horizontally when scroll, being it images, 9 * divs or what-have-you. Use this script to add background or foreground parallax 10 * effect to your website. 11 * 12 * Website: https://diegoversiani.me/smooth-parallax 13 * Github: https://github.com/diegoversiani/smooth-parallax 14 * 15 * Author: Diego Versiani 16 * Contact: https://diegoversiani.me/ 17 * 18 * Based on the work of: 19 * Rachel Smith: https://codepen.io/rachsmith/post/how-to-move-elements-on-scroll-in-a-way-that-doesn-t-suck-too-bad 20 */ 21(function (root, factory) { 22 if ( typeof define === 'function' && define.amd ) { 23 define([], factory(root)); 24 } else if ( typeof exports === 'object' ) { 25 module.exports = factory(root); 26 } else { 27 root.SmoothParallax = factory(root); 28 } 29})(typeof global !== 'undefined' ? global : this.window || this.global, function (root) { 30 31 'use strict'; 32 33 // 34 // Variables 35 // 36 37 var window = root; // Map window to root to avoid confusion 38 var _container; 39 var _width, _height, _scrollHeight, _viewPortHeight; 40 var _scrollPercent = 0; 41 var _scrollOffset = 0; 42 var _movingElements = []; 43 var _positions = []; 44 var _basePercentageOnOptions = [ 'containerVisibility', 'pageScroll' ]; 45 var _settings; 46 var publicMethods = {}; // Placeholder for public methods 47 48 // Default settings 49 var defaults = { 50 basePercentageOn: 'containerVisibility', // See `_basePercentageOnOptions` for more options 51 decimalPrecision: 2 52 }; 53 54 55 // 56 // Methods 57 // 58 59 /** 60 * Merge two or more objects. Returns a new object. 61 * @private 62 * @param {Boolean} deep If true, do a deep (or recursive) merge [optional] 63 * @param {Object} objects The objects to merge together 64 * @returns {Object} Merged values of defaults and options 65 */ 66 var extend = function () { 67 // Variables 68 var extended = {}; 69 var deep = false; 70 var i = 0; 71 var length = arguments.length; 72 73 // Check if a deep merge 74 if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) { 75 deep = arguments[0]; 76 i++; 77 } 78 79 // Merge the object into the extended object 80 var merge = function (obj) { 81 for ( var prop in obj ) { 82 if ( Object.prototype.hasOwnProperty.call( obj, prop ) ) { 83 // If deep merge and property is an object, merge properties 84 if ( deep && Object.prototype.toString.call(obj[prop]) === '[object Object]' ) { 85 extended[prop] = extend( true, extended[prop], obj[prop] ); 86 } else { 87 extended[prop] = obj[prop]; 88 } 89 } 90 } 91 }; 92 93 // Loop through each object and conduct a merge 94 for ( ; i < length; i++ ) { 95 var obj = arguments[i]; 96 merge(obj); 97 } 98 99 return extended; 100 }; 101 102 103 104 /** 105 * Get movable element container 106 * @private 107 */ 108 var getElementContainer = function ( element ) { 109 var containerSelector = element.getAttribute( 'container' ); 110 _container = element.parentNode; 111 112 if ( containerSelector != '' && document.querySelector( containerSelector ) ) { 113 _container = document.querySelector( containerSelector ); 114 } 115 116 return _container; 117 }; 118 119 120 121 /** 122 * Calculate page percent scrolled. 123 * @private 124 */ 125 var calculatePageScrollPercent = function () { 126 var documentElement = document.documentElement || document.body; 127 _height = documentElement.scrollHeight; 128 _scrollOffset = window.pageYOffset || documentElement.scrollTop; 129 return _scrollOffset / ( _height - documentElement.clientHeight ); 130 }; 131 132 133 134 /** 135 * Calculate variables used to determine elements position 136 * @private 137 */ 138 var calculatePercent = function ( positionData ) { 139 _viewPortHeight = window.innerHeight; 140 141 // Based on `containerVisibility` 142 if ( _settings.basePercentageOn == 'containerVisibility' ) { 143 _height = positionData.container.scrollHeight; 144 _scrollOffset = _viewPortHeight - positionData.container.getBoundingClientRect().top; 145 _scrollPercent = _scrollOffset / _height; 146 } 147 148 // Based on `pageScroll` 149 if ( _settings.basePercentageOn == 'pageScroll' ) { 150 _scrollPercent = calculatePageScrollPercent(); 151 } 152 153 // Normalize scrollPercentage from 0 to 1 154 if ( _scrollPercent < 0 ) { 155 _scrollPercent = 0; 156 } 157 else if ( _scrollPercent > 1 ) { 158 _scrollPercent = 1; 159 } 160 }; 161 162 163 164 /** 165 * Get position data object for the element. 166 * @returns {Object} Position data object for element or false if not found. 167 */ 168 var getPositionDataByElement = function ( el ) { 169 for (var i = 0; i < _positions.length; i++) { 170 if ( _positions[i].element == el ) { 171 return _positions[i]; 172 } 173 } 174 175 // Return false if not found 176 return false;
177 } 178 179 180 181 /** 182 * Initializes positions for each moving element. 183 * @private 184 */ 185 var initializeMovingElementsPosition = function () { 186 var startPercent, 187 startX, 188 startY, 189 endPercent, 190 endX, 191 endY, 192 baseSizeOn, 193 baseSizeOnOptions = [ 'elementsize', 'containerSize' ]; 194 195 _movingElements = document.querySelectorAll('[smooth-parallax]'); 196 197 for (var i = 0; i < _movingElements.length; i++) { 198 startPercent = parseFloat(_movingElements[i].getAttribute( 'start-movement' )) || 0; 199 startX = parseFloat(_movingElements[i].getAttribute( 'start-position-x' )) || 0; 200 startY = parseFloat(_movingElements[i].getAttribute( 'start-position-y' )) || 0; 201 endPercent = parseFloat(_movingElements[i].getAttribute( 'end-movement' )) || 1; 202 endX = parseFloat(_movingElements[i].getAttribute( 'end-position-x' )) || 0; 203 endY = parseFloat(_movingElements[i].getAttribute( 'end-position-y' )) || 0; 204 baseSizeOn = _movingElements[i].getAttribute( 'base-size' ); 205 206 if ( baseSizeOnOptions.indexOf( baseSizeOn ) == -1 ) { 207 baseSizeOn = 'elementSize'; // Default value 208 } 209 210 var elementPosition = { 211 element: _movingElements[i], 212 container: getElementContainer( _movingElements[i] ), 213 baseSizeOn: baseSizeOn, 214 start: { 215 percent: startPercent, 216 x: startX, 217 y: startY 218 }, 219 end: { 220 percent: endPercent, 221 x: endX, 222 y: endY 223 }, 224 diff: { 225 percent: endPercent - startPercent, 226 x: endX - startX, 227 y: endY - startY, 228 }, 229 target: {}, 230 current: {} 231 }; 232 233 _positions.push( elementPosition ); 234 } 235 }; 236 237 /** 238 * Updates moving elements position. 239 * @private 240 */ 241 var updateElementsPosition = function () { 242 for (var i = 0; i < _movingElements.length; i++) { 243 var p = _positions[i], 244 baseWidth, 245 baseHeight, 246 transformValue; 247 248 // Try get element's size with `scrollWidth` and `scrollHeight` 249 // otherwise use `getComputedStyle` which is more expensive 250 if ( p.baseSizeOn == 'elementSize' ) { 251 baseWidth = _movingElements[i].scrollWidth || parseFloat( window.getComputedStyle( _movingElements[i] ).width ); 252 baseHeight = _movingElements[i].scrollHeight || parseFloat( window.getComputedStyle( _movingElements[i] ).height ); 253 } 254 else if ( p.baseSizeOn == 'containerSize' ) { 255 baseWidth = p.container.scrollWidth - (_movingElements[i].scrollWidth || parseFloat( window.getComputedStyle( _movingElements[i] ).width ) ); 256 baseHeight = p.container.scrollHeight - (_movingElements[i].scrollHeight || parseFloat( window.getComputedStyle( _movingElements[i] ).height ) ); 257 } 258 259 // Need to calculate percentage for each element 260 // when based on `containerVisibility` 261 calculatePercent( p ); 262 263 // calculate target position 264 if(_scrollPercent <= p.start.percent) { 265 p.target.x = p.start.x * baseWidth; 266 p.target.y = p.start.y * baseHeight; 267 } 268 else if(_scrollPercent >= p.end.percent) {
269 p.target.x = p.end.x * baseWidth; 270 p.target.y = p.end.y * baseHeight; 271 } 272 else { 273 p.target.x = p.start.x * baseWidth + ( p.diff.x * ( _scrollPercent - p.start.percent ) / p.diff.percent * baseWidth ); 274 p.target.y = p.start.y * baseHeight + ( p.diff.y * ( _scrollPercent - p.start.percent ) / p.diff.percent * baseHeight ); 275 } 276 277 // easing with linear interpolation 278 if( !p.current.x || !p.current.y) { 279 p.current.x = p.target.x; 280 p.current.y = p.target.y; 281 } else { 282 p.current.x = p.current.x + (p.target.x - p.current.x) * 0.1; 283 p.current.y = p.current.y + (p.target.y - p.current.y) * 0.1; 284 } 285 286 // Round to decimal precision to prevent 287 // too many calculation trips 288 p.current.x = parseFloat( p.current.x.toFixed( _settings.decimalPrecision ) ); 289 p.current.y = parseFloat( p.current.y.toFixed( _settings.decimalPrecision ) ); 290 291 // update element style 292 _movingElements[i].style.transform = 'translate3d(' + p.current.x + 'px, ' + p.current.y + 'px, 0)'; 293 } 294 }; 295 296 /** 297 * Keep updating elements position infinitelly. 298 * @private 299 */ 300 var loopUpdatePositions = function () { 301 updateElementsPosition(); 302 requestAnimationFrame( loopUpdatePositions ); 303 }; 304 305 /** 306 * Keep updating elements position infinitelly. 307 * @private 308 */ 309 var isSupported = function () { 310 var supported = true; 311 312 // Test basePercentageOn settings 313 if ( _basePercentageOnOptions.indexOf( _settings.basePercentageOn ) == -1 ) { 314 supported = false; 315 console.error( 'Value not supported for setting basePercentageOn: ' + _settings.basePercentageOn ); 316 } 317 318 // TODO: ADD feature test for `querySelector` 319 // TODO: ADD feature test for css property `translate3d` 320 321 return supported; 322 }; 323 324 /** 325 * Initializes plugin 326 */ 327 publicMethods.init = function ( options ) { 328 // Merge user options with defaults 329 _settings = extend( defaults, options || {} ); 330 _settings.decimalPrecision = parseInt( _settings.decimalPrecision ) || defaults.decimalPrecision; 331 332 // Bail early if not supported 333 if ( !isSupported() ) { return; } 334 335 // Initialize variables 336 initializeMovingElementsPosition(); 337 loopUpdatePositions(); 338 }; 339 340 /** 341 * Get scroll percentage for the element or page. 342 * @param {string} el Target element css selector. 343 * @return {float} Scroll percentage for the element or the page. 344 */ 345 publicMethods.getScrollPercent = function ( selector ) { 346 // Calculate page scroll if no selector was passed 347 if ( selector == undefined ) { 348 return calculatePageScrollPercent(); 349 } 350 351 // Find element 352 // Return false if not found 353 var el = document.querySelector( selector ); 354 if ( el == null ) return false; 355 356 // Calculate element scroll percent 357 var positionData = getPositionDataByElement( el ); 358 if ( positionData ) { 359 calculatePercent( positionData ); 360 return _scrollPercent; 361 } 362 363 // Return false otherwise 364 return false; 365 }; 366 367 368 // 369 // Public APIs 370 // 371 return publicMethods; 372 373});
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.