1
|
/*! UIkit 2.27.5 | http://www.getuikit.com | (c) 2014 YOOtheme | MIT License */
|
2
|
(function(addon) {
|
3
|
|
4
|
var component;
|
5
|
|
6
|
if (window.UIkit2) {
|
7
|
component = addon(UIkit2);
|
8
|
}
|
9
|
|
10
|
if (typeof define == 'function' && define.amd) {
|
11
|
define('uikit-parallax', ['uikit'], function(){
|
12
|
return component || addon(UIkit2);
|
13
|
});
|
14
|
}
|
15
|
|
16
|
})(function(UI){
|
17
|
|
18
|
"use strict";
|
19
|
|
20
|
var parallaxes = [],
|
21
|
supports3d = false,
|
22
|
scrolltop = 0,
|
23
|
wh = window.innerHeight,
|
24
|
checkParallaxes = function() {
|
25
|
|
26
|
scrolltop = UI.$win.scrollTop();
|
27
|
|
28
|
window.requestAnimationFrame(function(){
|
29
|
for (var i=0; i < parallaxes.length; i++) {
|
30
|
parallaxes[i].process();
|
31
|
}
|
32
|
});
|
33
|
};
|
34
|
|
35
|
|
36
|
UI.component('parallax', {
|
37
|
|
38
|
defaults: {
|
39
|
velocity : 0.5,
|
40
|
target : false,
|
41
|
viewport : false,
|
42
|
media : false
|
43
|
},
|
44
|
|
45
|
boot: function() {
|
46
|
|
47
|
supports3d = (function(){
|
48
|
|
49
|
var el = document.createElement('div'),
|
50
|
has3d,
|
51
|
transforms = {
|
52
|
'WebkitTransform':'-webkit-transform',
|
53
|
'MSTransform':'-ms-transform',
|
54
|
'MozTransform':'-moz-transform',
|
55
|
'Transform':'transform'
|
56
|
};
|
57
|
|
58
|
// Add it to the body to get the computed style.
|
59
|
document.body.insertBefore(el, null);
|
60
|
|
61
|
for (var t in transforms) {
|
62
|
if (el.style[t] !== undefined) {
|
63
|
el.style[t] = 'translate3d(1px,1px,1px)';
|
64
|
has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]);
|
65
|
}
|
66
|
}
|
67
|
|
68
|
document.body.removeChild(el);
|
69
|
|
70
|
return (has3d !== undefined && has3d.length > 0 && has3d !== "none");
|
71
|
})();
|
72
|
|
73
|
// listen to scroll and resize
|
74
|
UI.$doc.on('scrolling.uk.document', checkParallaxes);
|
75
|
UI.$win.on('load resize orientationchange', UI.Utils.debounce(function(){
|
76
|
wh = window.innerHeight;
|
77
|
checkParallaxes();
|
78
|
}, 50));
|
79
|
|
80
|
// init code
|
81
|
UI.ready(function(context) {
|
82
|
|
83
|
UI.$('[data-uk-parallax]', context).each(function() {
|
84
|
|
85
|
var parallax = UI.$(this);
|
86
|
|
87
|
if (!parallax.data('parallax')) {
|
88
|
UI.parallax(parallax, UI.Utils.options(parallax.attr('data-uk-parallax')));
|
89
|
}
|
90
|
});
|
91
|
});
|
92
|
},
|
93
|
|
94
|
init: function() {
|
95
|
|
96
|
this.base = this.options.target ? UI.$(this.options.target) : this.element;
|
97
|
this.props = {};
|
98
|
this.velocity = (this.options.velocity || 1);
|
99
|
|
100
|
var reserved = ['target','velocity','viewport','plugins','media'];
|
101
|
|
102
|
Object.keys(this.options).forEach(function(prop){
|
103
|
|
104
|
if (reserved.indexOf(prop) !== -1) {
|
105
|
return;
|
106
|
}
|
107
|
|
108
|
var start, end, dir, diff, startend = String(this.options[prop]).split(',');
|
109
|
|
110
|
if (prop.match(/color/i)) {
|
111
|
start = startend[1] ? startend[0] : this._getStartValue(prop),
|
112
|
end = startend[1] ? startend[1] : startend[0];
|
113
|
|
114
|
if (!start) {
|
115
|
start = 'rgba(255,255,255,0)';
|
116
|
}
|
117
|
|
118
|
} else {
|
119
|
start = parseFloat(startend[1] ? startend[0] : this._getStartValue(prop)),
|
120
|
end = parseFloat(startend[1] ? startend[1] : startend[0]);
|
121
|
diff = (start < end ? (end-start):(start-end));
|
122
|
dir = (start < end ? 1:-1);
|
123
|
}
|
124
|
|
125
|
this.props[prop] = { start: start, end: end, dir: dir, diff: diff };
|
126
|
|
127
|
}.bind(this));
|
128
|
|
129
|
parallaxes.push(this);
|
130
|
},
|
131
|
|
132
|
process: function() {
|
133
|
|
134
|
if (this.options.media) {
|
135
|
|
136
|
switch(typeof(this.options.media)) {
|
137
|
case 'number':
|
138
|
if (window.innerWidth < this.options.media) {
|
139
|
return false;
|
140
|
}
|
141
|
break;
|
142
|
case 'string':
|
143
|
if (window.matchMedia && !window.matchMedia(this.options.media).matches) {
|
144
|
return false;
|
145
|
}
|
146
|
break;
|
147
|
}
|
148
|
}
|
149
|
|
150
|
var percent = this.percentageInViewport();
|
151
|
|
152
|
if (this.options.viewport !== false) {
|
153
|
percent = (this.options.viewport === 0) ? 1 : percent / this.options.viewport;
|
154
|
}
|
155
|
|
156
|
this.update(percent);
|
157
|
},
|
158
|
|
159
|
percentageInViewport: function() {
|
160
|
|
161
|
var top = this.base.offset().top,
|
162
|
height = this.base.outerHeight(),
|
163
|
distance, percentage, percent;
|
164
|
|
165
|
if (top > (scrolltop + wh)) {
|
166
|
percent = 0;
|
167
|
} else if ((top + height) < scrolltop) {
|
168
|
percent = 1;
|
169
|
} else {
|
170
|
|
171
|
if ((top + height) < wh) {
|
172
|
|
173
|
percent = (scrolltop < wh ? scrolltop : scrolltop - wh) / (top+height);
|
174
|
|
175
|
} else {
|
176
|
|
177
|
distance = (scrolltop + wh) - top;
|
178
|
percentage = Math.round(distance / ((wh + height) / 100));
|
179
|
percent = percentage/100;
|
180
|
}
|
181
|
}
|
182
|
|
183
|
return percent;
|
184
|
},
|
185
|
|
186
|
update: function(percent) {
|
187
|
|
188
|
var $this = this,
|
189
|
css = {transform:'', filter:''},
|
190
|
compercent = percent * (1 - (this.velocity - (this.velocity * percent))),
|
191
|
opts, val;
|
192
|
|
193
|
if (compercent < 0) compercent = 0;
|
194
|
if (compercent > 1) compercent = 1;
|
195
|
|
196
|
if (this._percent !== undefined && this._percent == compercent) {
|
197
|
return;
|
198
|
}
|
199
|
|
200
|
Object.keys(this.props).forEach(function(prop) {
|
201
|
|
202
|
opts = this.props[prop];
|
203
|
|
204
|
if (percent === 0) {
|
205
|
val = opts.start;
|
206
|
} else if(percent === 1) {
|
207
|
val = opts.end;
|
208
|
} else if(opts.diff !== undefined) {
|
209
|
val = opts.start + (opts.diff * compercent * opts.dir);
|
210
|
}
|
211
|
|
212
|
if ((prop == 'bg' || prop == 'bgp') && !this._bgcover) {
|
213
|
this._bgcover = initBgImageParallax(this, prop, opts);
|
214
|
}
|
215
|
|
216
|
switch(prop) {
|
217
|
|
218
|
// transforms
|
219
|
case 'x':
|
220
|
css.transform += supports3d ? ' translate3d('+val+'px, 0, 0)':' translateX('+val+'px)';
|
221
|
break;
|
222
|
case 'xp':
|
223
|
css.transform += supports3d ? ' translate3d('+val+'%, 0, 0)':' translateX('+val+'%)';
|
224
|
break;
|
225
|
case 'y':
|
226
|
css.transform += supports3d ? ' translate3d(0, '+val+'px, 0)':' translateY('+val+'px)';
|
227
|
break;
|
228
|
case 'yp':
|
229
|
css.transform += supports3d ? ' translate3d(0, '+val+'%, 0)':' translateY('+val+'%)';
|
230
|
break;
|
231
|
case 'rotate':
|
232
|
css.transform += ' rotate('+val+'deg)';
|
233
|
break;
|
234
|
case 'scale':
|
235
|
css.transform += ' scale('+val+')';
|
236
|
break;
|
237
|
|
238
|
// bg image
|
239
|
case 'bg':
|
240
|
|
241
|
// don't move if image height is too small
|
242
|
// if ($this.element.data('bgsize') && ($this.element.data('bgsize').h + val - window.innerHeight) < 0) {
|
243
|
// break;
|
244
|
// }
|
245
|
|
246
|
css['background-position'] = '50% '+val+'px';
|
247
|
break;
|
248
|
case 'bgp':
|
249
|
css['background-position'] = '50% '+val+'%';
|
250
|
break;
|
251
|
|
252
|
// color
|
253
|
case 'color':
|
254
|
case 'background-color':
|
255
|
case 'border-color':
|
256
|
css[prop] = calcColor(opts.start, opts.end, compercent);
|
257
|
break;
|
258
|
|
259
|
// CSS Filter
|
260
|
case 'blur':
|
261
|
css.filter += ' blur('+val+'px)';
|
262
|
break;
|
263
|
case 'hue':
|
264
|
css.filter += ' hue-rotate('+val+'deg)';
|
265
|
break;
|
266
|
case 'grayscale':
|
267
|
css.filter += ' grayscale('+val+'%)';
|
268
|
break;
|
269
|
case 'invert':
|
270
|
css.filter += ' invert('+val+'%)';
|
271
|
break;
|
272
|
case 'fopacity':
|
273
|
css.filter += ' opacity('+val+'%)';
|
274
|
break;
|
275
|
case 'saturate':
|
276
|
css.filter += ' saturate('+val+'%)';
|
277
|
break;
|
278
|
case 'sepia':
|
279
|
css.filter += ' sepia('+val+'%)';
|
280
|
break;
|
281
|
|
282
|
default:
|
283
|
css[prop] = val;
|
284
|
break;
|
285
|
}
|
286
|
|
287
|
}.bind(this));
|
288
|
|
289
|
if (css.filter) {
|
290
|
css['-webkit-filter'] = css.filter;
|
291
|
}
|
292
|
|
293
|
this.element.css(css);
|
294
|
|
295
|
this._percent = compercent;
|
296
|
},
|
297
|
|
298
|
_getStartValue: function(prop) {
|
299
|
|
300
|
var value = 0;
|
301
|
|
302
|
switch(prop) {
|
303
|
case 'scale':
|
304
|
value = 1;
|
305
|
break;
|
306
|
default:
|
307
|
value = this.element.css(prop);
|
308
|
}
|
309
|
|
310
|
return (value || 0);
|
311
|
}
|
312
|
|
313
|
});
|
314
|
|
315
|
|
316
|
// helper
|
317
|
|
318
|
function initBgImageParallax(obj, prop, opts) {
|
319
|
|
320
|
var img = new Image(), url, element, size, check, ratio, width, height;
|
321
|
|
322
|
element = obj.element.css({backgroundSize: 'cover', backgroundRepeat: 'no-repeat'});
|
323
|
url = element.css('background-image').replace(/^url\(/g, '').replace(/\)$/g, '').replace(/("|')/g, '');
|
324
|
check = function() {
|
325
|
|
326
|
var w = element.innerWidth(), h = element.innerHeight(), extra = (prop=='bg') ? opts.diff : (opts.diff/100) * h;
|
327
|
|
328
|
h += extra;
|
329
|
w += Math.ceil(extra * ratio);
|
330
|
|
331
|
if (w-extra < size.w && h < size.h) {
|
332
|
return obj.element.css({backgroundSize: 'auto'});
|
333
|
}
|
334
|
|
335
|
// if element height < parent height (gap underneath)
|
336
|
if ((w / ratio) < h) {
|
337
|
|
338
|
width = Math.ceil(h * ratio);
|
339
|
height = h;
|
340
|
|
341
|
if (h > window.innerHeight) {
|
342
|
width = width * 1.2;
|
343
|
height = height * 1.2;
|
344
|
}
|
345
|
|
346
|
// element width < parent width (gap to right)
|
347
|
} else {
|
348
|
|
349
|
width = w;
|
350
|
height = Math.ceil(w / ratio);
|
351
|
}
|
352
|
|
353
|
element.css({backgroundSize: (width+'px '+height+'px')}).data('bgsize', {w:width,h:height});
|
354
|
};
|
355
|
|
356
|
img.onerror = function(){
|
357
|
// image url doesn't exist
|
358
|
};
|
359
|
|
360
|
img.onload = function(){
|
361
|
size = {w:img.width, h:img.height};
|
362
|
ratio = img.width / img.height;
|
363
|
|
364
|
UI.$win.on('load resize orientationchange', UI.Utils.debounce(function(){
|
365
|
check();
|
366
|
}, 50));
|
367
|
|
368
|
check();
|
369
|
};
|
370
|
|
371
|
img.src = url;
|
372
|
|
373
|
return true;
|
374
|
}
|
375
|
|
376
|
|
377
|
// Some named colors to work with, added by Bradley Ayers
|
378
|
// From Interface by Stefan Petre
|
379
|
// http://interface.eyecon.ro/
|
380
|
var colors = {
|
381
|
'black': [0,0,0,1],
|
382
|
'blue': [0,0,255,1],
|
383
|
'brown': [165,42,42,1],
|
384
|
'cyan': [0,255,255,1],
|
385
|
'fuchsia': [255,0,255,1],
|
386
|
'gold': [255,215,0,1],
|
387
|
'green': [0,128,0,1],
|
388
|
'indigo': [75,0,130,1],
|
389
|
'khaki': [240,230,140,1],
|
390
|
'lime': [0,255,0,1],
|
391
|
'magenta': [255,0,255,1],
|
392
|
'maroon': [128,0,0,1],
|
393
|
'navy': [0,0,128,1],
|
394
|
'olive': [128,128,0,1],
|
395
|
'orange': [255,165,0,1],
|
396
|
'pink': [255,192,203,1],
|
397
|
'purple': [128,0,128,1],
|
398
|
'violet': [128,0,128,1],
|
399
|
'red': [255,0,0,1],
|
400
|
'silver': [192,192,192,1],
|
401
|
'white': [255,255,255,1],
|
402
|
'yellow': [255,255,0,1],
|
403
|
'transparent': [255,255,255,0]
|
404
|
};
|
405
|
|
406
|
function calcColor(start, end, pos) {
|
407
|
|
408
|
start = parseColor(start);
|
409
|
end = parseColor(end);
|
410
|
pos = pos || 0;
|
411
|
|
412
|
return calculateColor(start, end, pos);
|
413
|
}
|
414
|
|
415
|
/**!
|
416
|
* @preserve Color animation 1.6.0
|
417
|
* http://www.bitstorm.org/jquery/color-animation/
|
418
|
* Copyright 2011, 2013 Edwin Martin <edwin@bitstorm.org>
|
419
|
* Released under the MIT and GPL licenses.
|
420
|
*/
|
421
|
|
422
|
// Calculate an in-between color. Returns "#aabbcc"-like string.
|
423
|
function calculateColor(begin, end, pos) {
|
424
|
var color = 'rgba('
|
425
|
+ parseInt((begin[0] + pos * (end[0] - begin[0])), 10) + ','
|
426
|
+ parseInt((begin[1] + pos * (end[1] - begin[1])), 10) + ','
|
427
|
+ parseInt((begin[2] + pos * (end[2] - begin[2])), 10) + ','
|
428
|
+ (begin && end ? parseFloat(begin[3] + pos * (end[3] - begin[3])) : 1);
|
429
|
|
430
|
color += ')';
|
431
|
return color;
|
432
|
}
|
433
|
|
434
|
// Parse an CSS-syntax color. Outputs an array [r, g, b]
|
435
|
function parseColor(color) {
|
436
|
|
437
|
var match, quadruplet;
|
438
|
|
439
|
// Match #aabbcc
|
440
|
if (match = /#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})/.exec(color)) {
|
441
|
quadruplet = [parseInt(match[1], 16), parseInt(match[2], 16), parseInt(match[3], 16), 1];
|
442
|
|
443
|
// Match #abc
|
444
|
} else if (match = /#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])/.exec(color)) {
|
445
|
quadruplet = [parseInt(match[1], 16) * 17, parseInt(match[2], 16) * 17, parseInt(match[3], 16) * 17, 1];
|
446
|
|
447
|
// Match rgb(n, n, n)
|
448
|
} else if (match = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) {
|
449
|
quadruplet = [parseInt(match[1]), parseInt(match[2]), parseInt(match[3]), 1];
|
450
|
|
451
|
} else if (match = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9\.]*)\s*\)/.exec(color)) {
|
452
|
quadruplet = [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10),parseFloat(match[4])];
|
453
|
|
454
|
// No browser returns rgb(n%, n%, n%), so little reason to support this format.
|
455
|
} else {
|
456
|
quadruplet = colors[color] || [255,255,255,0];
|
457
|
}
|
458
|
return quadruplet;
|
459
|
}
|
460
|
|
461
|
return UI.parallax;
|
462
|
});
|