1
|
|
|
/*! |
2
|
|
|
* Stellar.js v0.6.5 |
3
|
|
|
* http://markdalgleish.com/projects/stellar.js |
4
|
|
|
* |
5
|
|
|
* Copyright 2018, Mark Dalgleish |
6
|
|
|
* This content is released under the MIT license |
7
|
|
|
* http://markdalgleish.mit-license.org |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
(function(factory) { |
11
|
|
|
if (typeof define === "function" && define.amd) { |
|
|
|
|
12
|
|
|
define(['jquery'], function($) { |
13
|
|
|
return factory($); |
14
|
|
|
}); |
15
|
|
|
} else if (typeof module === "object" && typeof module.exports === "object") { |
16
|
|
|
exports = factory(require('jquery')); |
17
|
|
|
} else { |
18
|
|
|
factory(jQuery); |
19
|
|
|
} |
20
|
|
|
})(function($) { |
21
|
|
|
|
22
|
|
|
var pluginName = 'stellar', |
23
|
|
|
defaults = { |
24
|
|
|
scrollProperty: 'scroll', |
25
|
|
|
positionProperty: 'position', |
26
|
|
|
horizontalScrolling: true, |
27
|
|
|
verticalScrolling: true, |
28
|
|
|
horizontalOffset: 0, |
29
|
|
|
verticalOffset: 0, |
30
|
|
|
responsive: false, |
31
|
|
|
parallaxBackgrounds: true, |
32
|
|
|
parallaxElements: true, |
33
|
|
|
hideDistantElements: true, |
34
|
|
|
hideElement: function($elem) { $elem.hide(); }, |
35
|
|
|
showElement: function($elem) { $elem.show(); } |
36
|
|
|
}, |
37
|
|
|
|
38
|
|
|
scrollProperty = { |
39
|
|
|
scroll: { |
40
|
|
|
getLeft: function($elem) { return $elem.scrollLeft(); }, |
41
|
|
|
setLeft: function($elem, val) { $elem.scrollLeft(val); }, |
42
|
|
|
|
43
|
|
|
getTop: function($elem) { return $elem.scrollTop(); }, |
44
|
|
|
setTop: function($elem, val) { $elem.scrollTop(val); } |
45
|
|
|
}, |
46
|
|
|
position: { |
47
|
|
|
getLeft: function($elem) { return parseInt($elem.css('left'), 10) * -1; }, |
48
|
|
|
getTop: function($elem) { return parseInt($elem.css('top'), 10) * -1; } |
49
|
|
|
}, |
50
|
|
|
margin: { |
51
|
|
|
getLeft: function($elem) { return parseInt($elem.css('margin-left'), 10) * -1; }, |
52
|
|
|
getTop: function($elem) { return parseInt($elem.css('margin-top'), 10) * -1; } |
53
|
|
|
}, |
54
|
|
|
transform: { |
55
|
|
|
getLeft: function($elem) { |
56
|
|
|
var computedTransform = getComputedStyle($elem[0])[prefixedTransform]; |
57
|
|
|
return (computedTransform !== 'none' ? parseInt(computedTransform.match(/(-?[0-9]+)/g)[4], 10) * -1 : 0); |
58
|
|
|
}, |
59
|
|
|
getTop: function($elem) { |
60
|
|
|
var computedTransform = getComputedStyle($elem[0])[prefixedTransform]; |
61
|
|
|
return (computedTransform !== 'none' ? parseInt(computedTransform.match(/(-?[0-9]+)/g)[5], 10) * -1 : 0); |
62
|
|
|
} |
63
|
|
|
} |
64
|
|
|
}, |
65
|
|
|
|
66
|
|
|
positionProperty = { |
67
|
|
|
position: { |
68
|
|
|
setLeft: function($elem, left) { $elem.css('left', left); }, |
69
|
|
|
setTop: function($elem, top) { $elem.css('top', top); } |
70
|
|
|
}, |
71
|
|
|
transform: { |
72
|
|
|
setPosition: function($elem, left, startingLeft, top, startingTop) { |
73
|
|
|
$elem[0].style[prefixedTransform] = 'translate3d(' + (left - startingLeft) + 'px, ' + (top - startingTop) + 'px, 0)'; |
74
|
|
|
} |
75
|
|
|
} |
76
|
|
|
}, |
77
|
|
|
|
78
|
|
|
// Returns a function which adds a vendor prefix to any CSS property name |
79
|
|
|
vendorPrefix = (function() { |
80
|
|
|
var prefixes = /^(Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/, |
81
|
|
|
style = $('script')[0].style, |
82
|
|
|
prefix = '', |
83
|
|
|
prop; |
84
|
|
|
|
85
|
|
|
for (prop in style) { |
86
|
|
|
if (prefixes.test(prop)) { |
87
|
|
|
prefix = prop.match(prefixes)[0]; |
88
|
|
|
break; |
89
|
|
|
} |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
if ('WebkitOpacity' in style) { prefix = 'Webkit'; } |
93
|
|
|
if ('KhtmlOpacity' in style) { prefix = 'Khtml'; } |
94
|
|
|
|
95
|
|
|
return function(property) { |
96
|
|
|
return prefix + (prefix.length > 0 ? property.charAt(0).toUpperCase() + property.slice(1) : property); |
97
|
|
|
}; |
98
|
|
|
}()), |
99
|
|
|
|
100
|
|
|
prefixedTransform = vendorPrefix('transform'), |
101
|
|
|
|
102
|
|
|
supportsBackgroundPositionXY = $('<div />', { style: 'background:#fff' }).css('background-position-x') !== undefined, |
103
|
|
|
|
104
|
|
|
setBackgroundPosition = (supportsBackgroundPositionXY ? |
105
|
|
|
function($elem, x, y) { |
106
|
|
|
$elem.css({ |
107
|
|
|
'background-position-x': x, |
108
|
|
|
'background-position-y': y |
109
|
|
|
}); |
110
|
|
|
} : |
111
|
|
|
function($elem, x, y) { |
112
|
|
|
$elem.css('background-position', x + ' ' + y); |
113
|
|
|
} |
114
|
|
|
), |
115
|
|
|
|
116
|
|
|
getBackgroundPosition = (supportsBackgroundPositionXY ? |
117
|
|
|
function($elem) { |
118
|
|
|
return [ |
119
|
|
|
$elem.css('background-position-x'), |
120
|
|
|
$elem.css('background-position-y') |
121
|
|
|
]; |
122
|
|
|
} : |
123
|
|
|
function($elem) { |
124
|
|
|
return $elem.css('background-position').split(' '); |
125
|
|
|
} |
126
|
|
|
), |
127
|
|
|
|
128
|
|
|
requestAnimFrame = ( |
129
|
|
|
window.requestAnimationFrame || |
130
|
|
|
window.webkitRequestAnimationFrame || |
131
|
|
|
window.mozRequestAnimationFrame || |
132
|
|
|
window.oRequestAnimationFrame || |
133
|
|
|
window.msRequestAnimationFrame || |
134
|
|
|
function(callback) { |
135
|
|
|
setTimeout(callback, 1000 / 60); |
136
|
|
|
} |
137
|
|
|
); |
138
|
|
|
|
139
|
|
|
function Plugin(element, options) { |
140
|
|
|
this.element = element; |
141
|
|
|
this.options = $.extend({}, defaults, options); |
142
|
|
|
|
143
|
|
|
this._defaults = defaults; |
144
|
|
|
this._name = pluginName; |
145
|
|
|
|
146
|
|
|
this.init(); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
Plugin.prototype = { |
150
|
|
|
init: function() { |
151
|
|
|
this.options.name = pluginName + '_' + Math.floor(Math.random() * 1e9); |
152
|
|
|
|
153
|
|
|
this._defineElements(); |
154
|
|
|
this._defineGetters(); |
155
|
|
|
this._defineSetters(); |
156
|
|
|
this._handleWindowLoadAndResize(); |
157
|
|
|
this._detectViewport(); |
158
|
|
|
|
159
|
|
|
this.refresh({ firstLoad: true }); |
160
|
|
|
|
161
|
|
|
if (this.options.scrollProperty === 'scroll') { |
162
|
|
|
this._handleScrollEvent(); |
163
|
|
|
} else { |
164
|
|
|
this._startAnimationLoop(); |
165
|
|
|
} |
166
|
|
|
}, |
167
|
|
|
_defineElements: function() { |
168
|
|
|
if (this.element === document.body) this.element = window; |
|
|
|
|
169
|
|
|
this.$scrollElement = $(this.element); |
170
|
|
|
this.$element = (this.element === window ? $('body') : this.$scrollElement); |
171
|
|
|
this.$viewportElement = (this.options.viewportElement !== undefined ? $(this.options.viewportElement) : (this.$scrollElement[0] === window || this.options.scrollProperty === 'scroll' ? this.$scrollElement : this.$scrollElement.parent())); |
172
|
|
|
}, |
173
|
|
|
_defineGetters: function() { |
174
|
|
|
var self = this, |
175
|
|
|
scrollPropertyAdapter = scrollProperty[self.options.scrollProperty]; |
176
|
|
|
|
177
|
|
|
this._getScrollLeft = function() { |
178
|
|
|
return scrollPropertyAdapter.getLeft(self.$scrollElement); |
179
|
|
|
}; |
180
|
|
|
|
181
|
|
|
this._getScrollTop = function() { |
182
|
|
|
return scrollPropertyAdapter.getTop(self.$scrollElement); |
183
|
|
|
}; |
184
|
|
|
}, |
185
|
|
|
_defineSetters: function() { |
186
|
|
|
var self = this, |
187
|
|
|
scrollPropertyAdapter = scrollProperty[self.options.scrollProperty], |
188
|
|
|
positionPropertyAdapter = positionProperty[self.options.positionProperty], |
189
|
|
|
setScrollLeft = scrollPropertyAdapter.setLeft, |
190
|
|
|
setScrollTop = scrollPropertyAdapter.setTop; |
191
|
|
|
|
192
|
|
|
this._setScrollLeft = (typeof setScrollLeft === 'function' ? function(val) { |
193
|
|
|
setScrollLeft(self.$scrollElement, val); |
194
|
|
|
} : $.noop); |
195
|
|
|
|
196
|
|
|
this._setScrollTop = (typeof setScrollTop === 'function' ? function(val) { |
197
|
|
|
setScrollTop(self.$scrollElement, val); |
198
|
|
|
} : $.noop); |
199
|
|
|
|
200
|
|
|
this._setPosition = positionPropertyAdapter.setPosition || |
201
|
|
|
function($elem, left, startingLeft, top, startingTop) { |
202
|
|
|
if (self.options.horizontalScrolling) { |
203
|
|
|
positionPropertyAdapter.setLeft($elem, left, startingLeft); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
if (self.options.verticalScrolling) { |
207
|
|
|
positionPropertyAdapter.setTop($elem, top, startingTop); |
208
|
|
|
} |
209
|
|
|
}; |
210
|
|
|
}, |
211
|
|
|
_handleWindowLoadAndResize: function() { |
212
|
|
|
var self = this, |
213
|
|
|
$window = $(window); |
214
|
|
|
|
215
|
|
|
if (self.options.responsive) { |
216
|
|
|
$window.bind('load.' + this.name, function() { |
217
|
|
|
self.refresh(); |
218
|
|
|
}); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
$window.bind('resize.' + this.name, function() { |
222
|
|
|
self._detectViewport(); |
223
|
|
|
|
224
|
|
|
if (self.options.responsive) { |
225
|
|
|
self.refresh(); |
226
|
|
|
} |
227
|
|
|
}); |
228
|
|
|
}, |
229
|
|
|
refresh: function(options) { |
230
|
|
|
var self = this, |
231
|
|
|
oldLeft = self._getScrollLeft(), |
232
|
|
|
oldTop = self._getScrollTop(); |
233
|
|
|
|
234
|
|
|
if (!options || !options.firstLoad) { |
235
|
|
|
this._reset(); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
this._setScrollLeft(0); |
239
|
|
|
this._setScrollTop(0); |
240
|
|
|
|
241
|
|
|
this._setOffsets(); |
242
|
|
|
this._findParticles(); |
243
|
|
|
this._findBackgrounds(); |
244
|
|
|
|
245
|
|
|
// Fix for WebKit background rendering bug |
246
|
|
|
if (options && options.firstLoad && /WebKit/.test(navigator.userAgent)) { |
|
|
|
|
247
|
|
|
$(window).on('load', function() { |
248
|
|
|
var oldLeft = self._getScrollLeft(), |
249
|
|
|
oldTop = self._getScrollTop(); |
250
|
|
|
|
251
|
|
|
self._setScrollLeft(oldLeft + 1); |
252
|
|
|
self._setScrollTop(oldTop + 1); |
253
|
|
|
|
254
|
|
|
self._setScrollLeft(oldLeft); |
255
|
|
|
self._setScrollTop(oldTop); |
256
|
|
|
}); |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
this._setScrollLeft(oldLeft); |
260
|
|
|
this._setScrollTop(oldTop); |
261
|
|
|
}, |
262
|
|
|
_detectViewport: function() { |
263
|
|
|
var viewportOffsets = this.$viewportElement[0] !== window ? this.$viewportElement.offset() : { top: 0, left: 0 }, |
264
|
|
|
hasOffsets = viewportOffsets !== null && viewportOffsets !== undefined; |
265
|
|
|
|
266
|
|
|
this.viewportWidth = this.$viewportElement.width(); |
267
|
|
|
this.viewportHeight = this.$viewportElement.height(); |
268
|
|
|
|
269
|
|
|
this.viewportOffsetTop = (hasOffsets ? viewportOffsets.top : 0); |
270
|
|
|
this.viewportOffsetLeft = (hasOffsets ? viewportOffsets.left : 0); |
271
|
|
|
}, |
272
|
|
|
_findParticles: function() { |
273
|
|
|
var self = this, |
274
|
|
|
scrollLeft = this._getScrollLeft(), |
|
|
|
|
275
|
|
|
scrollTop = this._getScrollTop(); |
|
|
|
|
276
|
|
|
|
277
|
|
|
if (this.particles !== undefined) { |
278
|
|
|
for (var i = this.particles.length - 1; i >= 0; i--) { |
279
|
|
|
this.particles[i].$element.data('stellar-elementIsActive', undefined); |
280
|
|
|
} |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
this.particles = []; |
284
|
|
|
|
285
|
|
|
if (!this.options.parallaxElements) return; |
|
|
|
|
286
|
|
|
|
287
|
|
|
this.$element.find('[data-stellar-ratio]').each(function(i) { |
|
|
|
|
288
|
|
|
var $this = $(this), |
289
|
|
|
horizontalOffset, |
290
|
|
|
verticalOffset, |
291
|
|
|
positionLeft, |
292
|
|
|
positionTop, |
293
|
|
|
marginLeft, |
294
|
|
|
marginTop, |
295
|
|
|
$offsetParent, |
296
|
|
|
offsetLeft, |
297
|
|
|
offsetTop, |
298
|
|
|
parentOffsetLeft = 0, |
299
|
|
|
parentOffsetTop = 0, |
300
|
|
|
tempParentOffsetLeft = 0, |
301
|
|
|
tempParentOffsetTop = 0; |
302
|
|
|
|
303
|
|
|
// Ensure this element isn't already part of another scrolling element |
304
|
|
|
if (!$this.data('stellar-elementIsActive')) { |
305
|
|
|
$this.data('stellar-elementIsActive', this); |
306
|
|
|
} else if ($this.data('stellar-elementIsActive') !== this) { |
307
|
|
|
return; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
self.options.showElement($this); |
311
|
|
|
|
312
|
|
|
// Save/restore the original top and left CSS values in case we refresh the particles or destroy the instance |
313
|
|
|
if (!$this.data('stellar-startingLeft')) { |
314
|
|
|
$this.data('stellar-startingLeft', $this.css('left')); |
315
|
|
|
$this.data('stellar-startingTop', $this.css('top')); |
316
|
|
|
} else { |
317
|
|
|
$this.css('left', $this.data('stellar-startingLeft')); |
318
|
|
|
$this.css('top', $this.data('stellar-startingTop')); |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
positionLeft = $this.position().left; |
322
|
|
|
positionTop = $this.position().top; |
323
|
|
|
|
324
|
|
|
// Catch-all for margin top/left properties (these evaluate to 'auto' in IE7 and IE8) |
325
|
|
|
marginLeft = ($this.css('margin-left') === 'auto') ? 0 : parseInt($this.css('margin-left'), 10); |
326
|
|
|
marginTop = ($this.css('margin-top') === 'auto') ? 0 : parseInt($this.css('margin-top'), 10); |
327
|
|
|
|
328
|
|
|
offsetLeft = $this.offset().left - marginLeft; |
329
|
|
|
offsetTop = $this.offset().top - marginTop; |
330
|
|
|
|
331
|
|
|
// Calculate the offset parent |
332
|
|
|
$this.parents().each(function() { |
333
|
|
|
var $this = $(this); |
334
|
|
|
|
335
|
|
|
if ($this.data('stellar-offset-parent') === true) { |
336
|
|
|
parentOffsetLeft = tempParentOffsetLeft; |
337
|
|
|
parentOffsetTop = tempParentOffsetTop; |
338
|
|
|
$offsetParent = $this; |
339
|
|
|
|
340
|
|
|
return false; |
341
|
|
|
} else { |
|
|
|
|
342
|
|
|
tempParentOffsetLeft += $this.position().left; |
343
|
|
|
tempParentOffsetTop += $this.position().top; |
|
|
|
|
344
|
|
|
} |
345
|
|
|
}); |
346
|
|
|
|
347
|
|
|
// Detect the offsets |
348
|
|
|
horizontalOffset = ($this.data('stellar-horizontal-offset') !== undefined ? $this.data('stellar-horizontal-offset') : ($offsetParent !== undefined && $offsetParent.data('stellar-horizontal-offset') !== undefined ? $offsetParent.data('stellar-horizontal-offset') : self.horizontalOffset)); |
349
|
|
|
verticalOffset = ($this.data('stellar-vertical-offset') !== undefined ? $this.data('stellar-vertical-offset') : ($offsetParent !== undefined && $offsetParent.data('stellar-vertical-offset') !== undefined ? $offsetParent.data('stellar-vertical-offset') : self.verticalOffset)); |
350
|
|
|
|
351
|
|
|
// Add our object to the particles collection |
352
|
|
|
self.particles.push({ |
353
|
|
|
$element: $this, |
354
|
|
|
$offsetParent: $offsetParent, |
355
|
|
|
isFixed: $this.css('position') === 'fixed', |
356
|
|
|
horizontalOffset: horizontalOffset, |
357
|
|
|
verticalOffset: verticalOffset, |
358
|
|
|
startingPositionLeft: positionLeft, |
359
|
|
|
startingPositionTop: positionTop, |
360
|
|
|
startingOffsetLeft: offsetLeft, |
361
|
|
|
startingOffsetTop: offsetTop, |
362
|
|
|
parentOffsetLeft: parentOffsetLeft, |
363
|
|
|
parentOffsetTop: parentOffsetTop, |
364
|
|
|
stellarRatio: ($this.data('stellar-ratio') !== undefined ? $this.data('stellar-ratio') : 1), |
365
|
|
|
width: $this.outerWidth(true), |
366
|
|
|
height: $this.outerHeight(true), |
367
|
|
|
isHidden: false |
368
|
|
|
}); |
369
|
|
|
}); |
370
|
|
|
}, |
371
|
|
|
_findBackgrounds: function() { |
372
|
|
|
var self = this, |
373
|
|
|
scrollLeft = this._getScrollLeft(), |
374
|
|
|
scrollTop = this._getScrollTop(), |
375
|
|
|
$backgroundElements; |
376
|
|
|
|
377
|
|
|
this.backgrounds = []; |
378
|
|
|
|
379
|
|
|
if (!this.options.parallaxBackgrounds) return; |
|
|
|
|
380
|
|
|
|
381
|
|
|
$backgroundElements = this.$element.find('[data-stellar-background-ratio]'); |
382
|
|
|
|
383
|
|
|
if (this.$element.data('stellar-background-ratio')) { |
384
|
|
|
$backgroundElements = $backgroundElements.add(this.$element); |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
$backgroundElements.each(function() { |
388
|
|
|
var $this = $(this), |
389
|
|
|
backgroundPosition = getBackgroundPosition($this), |
390
|
|
|
horizontalOffset, |
391
|
|
|
verticalOffset, |
392
|
|
|
positionLeft, |
|
|
|
|
393
|
|
|
positionTop, |
|
|
|
|
394
|
|
|
marginLeft, |
395
|
|
|
marginTop, |
396
|
|
|
offsetLeft, |
397
|
|
|
offsetTop, |
398
|
|
|
$offsetParent, |
399
|
|
|
parentOffsetLeft = 0, |
400
|
|
|
parentOffsetTop = 0, |
401
|
|
|
tempParentOffsetLeft = 0, |
402
|
|
|
tempParentOffsetTop = 0; |
403
|
|
|
|
404
|
|
|
// Ensure this element isn't already part of another scrolling element |
405
|
|
|
if (!$this.data('stellar-backgroundIsActive')) { |
406
|
|
|
$this.data('stellar-backgroundIsActive', this); |
407
|
|
|
} else if ($this.data('stellar-backgroundIsActive') !== this) { |
408
|
|
|
return; |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
// Save/restore the original top and left CSS values in case we destroy the instance |
412
|
|
|
if (!$this.data('stellar-backgroundStartingLeft')) { |
413
|
|
|
$this.data('stellar-backgroundStartingLeft', backgroundPosition[0]); |
414
|
|
|
$this.data('stellar-backgroundStartingTop', backgroundPosition[1]); |
415
|
|
|
} else { |
416
|
|
|
setBackgroundPosition($this, $this.data('stellar-backgroundStartingLeft'), $this.data('stellar-backgroundStartingTop')); |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
// Catch-all for margin top/left properties (these evaluate to 'auto' in IE7 and IE8) |
420
|
|
|
marginLeft = ($this.css('margin-left') === 'auto') ? 0 : parseInt($this.css('margin-left'), 10); |
421
|
|
|
marginTop = ($this.css('margin-top') === 'auto') ? 0 : parseInt($this.css('margin-top'), 10); |
422
|
|
|
|
423
|
|
|
offsetLeft = $this.offset().left - marginLeft - scrollLeft; |
424
|
|
|
offsetTop = $this.offset().top - marginTop - scrollTop; |
425
|
|
|
|
426
|
|
|
// Calculate the offset parent |
427
|
|
|
$this.parents().each(function() { |
428
|
|
|
var $this = $(this); |
429
|
|
|
|
430
|
|
|
if ($this.data('stellar-offset-parent') === true) { |
431
|
|
|
parentOffsetLeft = tempParentOffsetLeft; |
432
|
|
|
parentOffsetTop = tempParentOffsetTop; |
433
|
|
|
$offsetParent = $this; |
434
|
|
|
|
435
|
|
|
return false; |
436
|
|
|
} else { |
|
|
|
|
437
|
|
|
tempParentOffsetLeft += $this.position().left; |
438
|
|
|
tempParentOffsetTop += $this.position().top; |
|
|
|
|
439
|
|
|
} |
440
|
|
|
}); |
441
|
|
|
|
442
|
|
|
// Detect the offsets |
443
|
|
|
horizontalOffset = ($this.data('stellar-horizontal-offset') !== undefined ? $this.data('stellar-horizontal-offset') : ($offsetParent !== undefined && $offsetParent.data('stellar-horizontal-offset') !== undefined ? $offsetParent.data('stellar-horizontal-offset') : self.horizontalOffset)); |
444
|
|
|
verticalOffset = ($this.data('stellar-vertical-offset') !== undefined ? $this.data('stellar-vertical-offset') : ($offsetParent !== undefined && $offsetParent.data('stellar-vertical-offset') !== undefined ? $offsetParent.data('stellar-vertical-offset') : self.verticalOffset)); |
445
|
|
|
|
446
|
|
|
self.backgrounds.push({ |
447
|
|
|
$element: $this, |
448
|
|
|
$offsetParent: $offsetParent, |
449
|
|
|
isFixed: $this.css('background-attachment') === 'fixed', |
450
|
|
|
horizontalOffset: horizontalOffset, |
451
|
|
|
verticalOffset: verticalOffset, |
452
|
|
|
startingValueLeft: backgroundPosition[0], |
453
|
|
|
startingValueTop: backgroundPosition[1], |
454
|
|
|
startingBackgroundPositionLeft: (isNaN(parseInt(backgroundPosition[0], 10)) ? 0 : parseInt(backgroundPosition[0], 10)), |
455
|
|
|
startingBackgroundPositionTop: (isNaN(parseInt(backgroundPosition[1], 10)) ? 0 : parseInt(backgroundPosition[1], 10)), |
456
|
|
|
startingPositionLeft: $this.position().left, |
457
|
|
|
startingPositionTop: $this.position().top, |
458
|
|
|
startingOffsetLeft: offsetLeft, |
459
|
|
|
startingOffsetTop: offsetTop, |
460
|
|
|
parentOffsetLeft: parentOffsetLeft, |
461
|
|
|
parentOffsetTop: parentOffsetTop, |
462
|
|
|
stellarRatio: ($this.data('stellar-background-ratio') === undefined ? 1 : $this.data('stellar-background-ratio')) |
463
|
|
|
}); |
464
|
|
|
}); |
465
|
|
|
}, |
466
|
|
|
_reset: function() { |
467
|
|
|
var particle, |
468
|
|
|
startingPositionLeft, |
469
|
|
|
startingPositionTop, |
470
|
|
|
background, |
471
|
|
|
i; |
472
|
|
|
|
473
|
|
|
for (i = this.particles.length - 1; i >= 0; i--) { |
474
|
|
|
particle = this.particles[i]; |
475
|
|
|
startingPositionLeft = particle.$element.data('stellar-startingLeft'); |
476
|
|
|
startingPositionTop = particle.$element.data('stellar-startingTop'); |
477
|
|
|
|
478
|
|
|
this._setPosition(particle.$element, startingPositionLeft, startingPositionLeft, startingPositionTop, startingPositionTop); |
479
|
|
|
|
480
|
|
|
this.options.showElement(particle.$element); |
481
|
|
|
|
482
|
|
|
particle.$element.data('stellar-startingLeft', null).data('stellar-elementIsActive', null).data('stellar-backgroundIsActive', null); |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
for (i = this.backgrounds.length - 1; i >= 0; i--) { |
486
|
|
|
background = this.backgrounds[i]; |
487
|
|
|
|
488
|
|
|
background.$element.data('stellar-backgroundStartingLeft', null).data('stellar-backgroundStartingTop', null); |
489
|
|
|
|
490
|
|
|
setBackgroundPosition(background.$element, background.startingValueLeft, background.startingValueTop); |
491
|
|
|
} |
492
|
|
|
}, |
493
|
|
|
destroy: function() { |
494
|
|
|
this._reset(); |
495
|
|
|
|
496
|
|
|
this.$scrollElement.unbind('resize.' + this.name).unbind('scroll.' + this.name); |
497
|
|
|
this._animationLoop = $.noop; |
498
|
|
|
|
499
|
|
|
$(window).unbind('load.' + this.name).unbind('resize.' + this.name); |
500
|
|
|
}, |
501
|
|
|
_setOffsets: function() { |
502
|
|
|
var self = this, |
503
|
|
|
$window = $(window); |
504
|
|
|
|
505
|
|
|
$window.unbind('resize.horizontal-' + this.name).unbind('resize.vertical-' + this.name); |
506
|
|
|
|
507
|
|
|
if (typeof this.options.horizontalOffset === 'function') { |
508
|
|
|
this.horizontalOffset = this.options.horizontalOffset(); |
509
|
|
|
$window.bind('resize.horizontal-' + this.name, function() { |
510
|
|
|
self.horizontalOffset = self.options.horizontalOffset(); |
511
|
|
|
}); |
512
|
|
|
} else { |
513
|
|
|
this.horizontalOffset = this.options.horizontalOffset; |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
if (typeof this.options.verticalOffset === 'function') { |
517
|
|
|
this.verticalOffset = this.options.verticalOffset(); |
518
|
|
|
$window.bind('resize.vertical-' + this.name, function() { |
519
|
|
|
self.verticalOffset = self.options.verticalOffset(); |
520
|
|
|
}); |
521
|
|
|
} else { |
522
|
|
|
this.verticalOffset = this.options.verticalOffset; |
523
|
|
|
} |
524
|
|
|
}, |
525
|
|
|
_repositionElements: function() { |
526
|
|
|
var scrollLeft = this._getScrollLeft(), |
527
|
|
|
scrollTop = this._getScrollTop(), |
528
|
|
|
horizontalOffset, |
529
|
|
|
verticalOffset, |
530
|
|
|
particle, |
531
|
|
|
fixedRatioOffset, |
532
|
|
|
background, |
533
|
|
|
bgLeft, |
534
|
|
|
bgTop, |
535
|
|
|
isVisibleVertical = true, |
536
|
|
|
isVisibleHorizontal = true, |
537
|
|
|
newPositionLeft, |
538
|
|
|
newPositionTop, |
539
|
|
|
newOffsetLeft, |
540
|
|
|
newOffsetTop, |
541
|
|
|
i; |
542
|
|
|
|
543
|
|
|
// First check that the scroll position or container size has changed |
544
|
|
|
if (this.currentScrollLeft === scrollLeft && this.currentScrollTop === scrollTop && this.currentWidth === this.viewportWidth && this.currentHeight === this.viewportHeight) { |
545
|
|
|
return; |
546
|
|
|
} else { |
|
|
|
|
547
|
|
|
this.currentScrollLeft = scrollLeft; |
548
|
|
|
this.currentScrollTop = scrollTop; |
549
|
|
|
this.currentWidth = this.viewportWidth; |
550
|
|
|
this.currentHeight = this.viewportHeight; |
551
|
|
|
} |
552
|
|
|
|
553
|
|
|
// Reposition elements |
554
|
|
|
for (i = this.particles.length - 1; i >= 0; i--) { |
555
|
|
|
particle = this.particles[i]; |
556
|
|
|
|
557
|
|
|
fixedRatioOffset = (particle.isFixed ? 1 : 0); |
558
|
|
|
|
559
|
|
|
// Calculate position, then calculate what the particle's new offset will be (for visibility check) |
560
|
|
|
if (this.options.horizontalScrolling) { |
561
|
|
|
newPositionLeft = (scrollLeft + particle.horizontalOffset + this.viewportOffsetLeft + particle.startingPositionLeft - particle.startingOffsetLeft + particle.parentOffsetLeft) * -(particle.stellarRatio + fixedRatioOffset - 1) + particle.startingPositionLeft; |
562
|
|
|
newOffsetLeft = newPositionLeft - particle.startingPositionLeft + particle.startingOffsetLeft; |
563
|
|
|
} else { |
564
|
|
|
newPositionLeft = particle.startingPositionLeft; |
565
|
|
|
newOffsetLeft = particle.startingOffsetLeft; |
566
|
|
|
} |
567
|
|
|
|
568
|
|
|
if (this.options.verticalScrolling) { |
569
|
|
|
newPositionTop = (scrollTop + particle.verticalOffset + this.viewportOffsetTop + particle.startingPositionTop - particle.startingOffsetTop + particle.parentOffsetTop) * -(particle.stellarRatio + fixedRatioOffset - 1) + particle.startingPositionTop; |
570
|
|
|
newOffsetTop = newPositionTop - particle.startingPositionTop + particle.startingOffsetTop; |
571
|
|
|
} else { |
572
|
|
|
newPositionTop = particle.startingPositionTop; |
573
|
|
|
newOffsetTop = particle.startingOffsetTop; |
574
|
|
|
} |
575
|
|
|
|
576
|
|
|
// Check visibility |
577
|
|
|
if (this.options.hideDistantElements) { |
578
|
|
|
isVisibleHorizontal = !this.options.horizontalScrolling || newOffsetLeft + particle.width > (particle.isFixed ? 0 : scrollLeft) && newOffsetLeft < (particle.isFixed ? 0 : scrollLeft) + this.viewportWidth + this.viewportOffsetLeft; |
579
|
|
|
isVisibleVertical = !this.options.verticalScrolling || newOffsetTop + particle.height > (particle.isFixed ? 0 : scrollTop) && newOffsetTop < (particle.isFixed ? 0 : scrollTop) + this.viewportHeight + this.viewportOffsetTop; |
580
|
|
|
} |
581
|
|
|
|
582
|
|
|
if (isVisibleHorizontal && isVisibleVertical) { |
583
|
|
|
if (particle.isHidden) { |
584
|
|
|
this.options.showElement(particle.$element); |
585
|
|
|
particle.isHidden = false; |
586
|
|
|
} |
587
|
|
|
|
588
|
|
|
this._setPosition(particle.$element, newPositionLeft, particle.startingPositionLeft, newPositionTop, particle.startingPositionTop); |
589
|
|
|
} else { |
590
|
|
|
if (!particle.isHidden) { |
591
|
|
|
this.options.hideElement(particle.$element); |
592
|
|
|
particle.isHidden = true; |
593
|
|
|
} |
594
|
|
|
} |
595
|
|
|
} |
596
|
|
|
|
597
|
|
|
// Reposition backgrounds |
598
|
|
|
for (i = this.backgrounds.length - 1; i >= 0; i--) { |
599
|
|
|
background = this.backgrounds[i]; |
600
|
|
|
|
601
|
|
|
fixedRatioOffset = (background.isFixed ? 0 : 1); |
602
|
|
|
bgLeft = (this.options.horizontalScrolling ? (scrollLeft + background.horizontalOffset - this.viewportOffsetLeft - background.startingOffsetLeft + background.parentOffsetLeft - background.startingBackgroundPositionLeft) * (fixedRatioOffset - background.stellarRatio) + 'px' : background.startingValueLeft); |
603
|
|
|
bgTop = (this.options.verticalScrolling ? (scrollTop + background.verticalOffset - this.viewportOffsetTop - background.startingOffsetTop + background.parentOffsetTop - background.startingBackgroundPositionTop) * (fixedRatioOffset - background.stellarRatio) + 'px' : background.startingValueTop); |
604
|
|
|
|
605
|
|
|
setBackgroundPosition(background.$element, bgLeft, bgTop); |
606
|
|
|
} |
607
|
|
|
}, |
608
|
|
|
_handleScrollEvent: function() { |
609
|
|
|
var self = this, |
610
|
|
|
ticking = false; |
611
|
|
|
|
612
|
|
|
var update = function() { |
613
|
|
|
self._repositionElements(); |
614
|
|
|
ticking = false; |
615
|
|
|
}; |
616
|
|
|
|
617
|
|
|
var requestTick = function() { |
618
|
|
|
if (!ticking) { |
619
|
|
|
requestAnimFrame(update); |
620
|
|
|
ticking = true; |
621
|
|
|
} |
622
|
|
|
}; |
623
|
|
|
|
624
|
|
|
this.$scrollElement.bind('scroll.' + this.name, requestTick); |
625
|
|
|
requestTick(); |
626
|
|
|
}, |
627
|
|
|
_startAnimationLoop: function() { |
628
|
|
|
var self = this; |
629
|
|
|
|
630
|
|
|
this._animationLoop = function() { |
631
|
|
|
requestAnimFrame(self._animationLoop); |
632
|
|
|
self._repositionElements(); |
633
|
|
|
}; |
634
|
|
|
this._animationLoop(); |
635
|
|
|
} |
636
|
|
|
}; |
637
|
|
|
|
638
|
|
|
$.fn[pluginName] = function(options) { |
639
|
|
|
var args = arguments; |
640
|
|
|
if (options === undefined || typeof options === 'object') { |
641
|
|
|
return this.each(function() { |
642
|
|
|
if (!$.data(this, 'plugin_' + pluginName)) { |
643
|
|
|
$.data(this, 'plugin_' + pluginName, new Plugin(this, options)); |
644
|
|
|
} |
645
|
|
|
}); |
646
|
|
|
} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') { |
|
|
|
|
647
|
|
|
return this.each(function() { |
648
|
|
|
var instance = $.data(this, 'plugin_' + pluginName); |
649
|
|
|
if (instance instanceof Plugin && typeof instance[options] === 'function') { |
650
|
|
|
instance[options].apply(instance, Array.prototype.slice.call(args, 1)); |
651
|
|
|
} |
652
|
|
|
if (options === 'destroy') { |
653
|
|
|
$.data(this, 'plugin_' + pluginName, null); |
654
|
|
|
} |
655
|
|
|
}); |
656
|
|
|
} |
657
|
|
|
}; |
658
|
|
|
|
659
|
|
|
$[pluginName] = function(options) { |
|
|
|
|
660
|
|
|
var $window = $(window); |
661
|
|
|
return $window.stellar.apply($window, Array.prototype.slice.call(arguments, 0)); |
662
|
|
|
}; |
663
|
|
|
|
664
|
|
|
// Expose the scroll and position property function hashes so they can be extended |
665
|
|
|
$[pluginName].scrollProperty = scrollProperty; |
666
|
|
|
$[pluginName].positionProperty = positionProperty; |
667
|
|
|
|
668
|
|
|
// Expose the plugin class so it can be modified |
669
|
|
|
window.Stellar = Plugin; |
670
|
|
|
}); |
This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.
To learn more about declaring variables in Javascript, see the MDN.