summaryrefslogtreecommitdiff
path: root/js/components/parallax.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/components/parallax.js')
-rwxr-xr-xjs/components/parallax.js462
1 files changed, 462 insertions, 0 deletions
diff --git a/js/components/parallax.js b/js/components/parallax.js
new file mode 100755
index 0000000..e706abb
--- /dev/null
+++ b/js/components/parallax.js
@@ -0,0 +1,462 @@
1/*! UIkit 2.26.4 | http://www.getuikit.com | (c) 2014 YOOtheme | MIT License */
2(function(addon) {
3
4 var component;
5
6 if (window.UIkit) {
7 component = addon(UIkit);
8 }
9
10 if (typeof define == "function" && define.amd) {
11 define("uikit-parallax", ["uikit"], function(){
12 return component || addon(UIkit);
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({'background-size': 'cover', 'background-repeat': '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({'background-size': '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({'background-size': (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});