Component-Builder/media/uikit/js/components/parallax.js

436 lines
14 KiB
JavaScript

/*! UIkit 2.25.0 | http://www.getuikit.com | (c) 2014 YOOtheme | MIT License */
(function(addon) {
var component;
if (window.UIkit) {
component = addon(UIkit);
}
if (typeof define == "function" && define.amd) {
define("uikit-parallax", ["uikit"], function(){
return component || addon(UIkit);
});
}
})(function(UI){
"use strict";
var parallaxes = [],
supports3d = false,
scrolltop = 0,
wh = window.innerHeight,
checkParallaxes = function() {
scrolltop = UI.$win.scrollTop();
window.requestAnimationFrame(function(){
for (var i=0; i < parallaxes.length; i++) {
parallaxes[i].process();
}
});
};
UI.component('parallax', {
defaults: {
velocity : 0.5,
target : false,
viewport : false,
media : false
},
boot: function() {
supports3d = (function(){
var el = document.createElement('div'),
has3d,
transforms = {
'WebkitTransform':'-webkit-transform',
'MSTransform':'-ms-transform',
'MozTransform':'-moz-transform',
'Transform':'transform'
};
// Add it to the body to get the computed style.
document.body.insertBefore(el, null);
for (var t in transforms) {
if (el.style[t] !== undefined) {
el.style[t] = "translate3d(1px,1px,1px)";
has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]);
}
}
document.body.removeChild(el);
return (has3d !== undefined && has3d.length > 0 && has3d !== "none");
})();
// listen to scroll and resize
UI.$doc.on("scrolling.uk.document", checkParallaxes);
UI.$win.on("load resize orientationchange", UI.Utils.debounce(function(){
wh = window.innerHeight;
checkParallaxes();
}, 50));
// init code
UI.ready(function(context) {
UI.$('[data-uk-parallax]', context).each(function() {
var parallax = UI.$(this);
if (!parallax.data("parallax")) {
UI.parallax(parallax, UI.Utils.options(parallax.attr("data-uk-parallax")));
}
});
});
},
init: function() {
this.base = this.options.target ? UI.$(this.options.target) : this.element;
this.props = {};
this.velocity = (this.options.velocity || 1);
var reserved = ['target','velocity','viewport','plugins','media'];
Object.keys(this.options).forEach(function(prop){
if (reserved.indexOf(prop) !== -1) {
return;
}
var start, end, dir, diff, startend = String(this.options[prop]).split(',');
if (prop.match(/color/i)) {
start = startend[1] ? startend[0] : this._getStartValue(prop),
end = startend[1] ? startend[1] : startend[0];
if (!start) {
start = 'rgba(255,255,255,0)';
}
} else {
start = parseFloat(startend[1] ? startend[0] : this._getStartValue(prop)),
end = parseFloat(startend[1] ? startend[1] : startend[0]);
diff = (start < end ? (end-start):(start-end));
dir = (start < end ? 1:-1);
}
this.props[prop] = { 'start': start, 'end': end, 'dir': dir, 'diff': diff };
}.bind(this));
parallaxes.push(this);
},
process: function() {
if (this.options.media) {
switch(typeof(this.options.media)) {
case 'number':
if (window.innerWidth < this.options.media) {
return false;
}
break;
case 'string':
if (window.matchMedia && !window.matchMedia(this.options.media).matches) {
return false;
}
break;
}
}
var percent = this.percentageInViewport();
if (this.options.viewport !== false) {
percent = (this.options.viewport === 0) ? 1 : percent / this.options.viewport;
}
this.update(percent);
},
percentageInViewport: function() {
var top = this.base.offset().top,
height = this.base.outerHeight(),
distance, percentage, percent;
if (top > (scrolltop + wh)) {
percent = 0;
} else if ((top + height) < scrolltop) {
percent = 1;
} else {
if ((top + height) < wh) {
percent = (scrolltop < wh ? scrolltop : scrolltop - wh) / (top+height);
} else {
distance = (scrolltop + wh) - top;
percentage = Math.round(distance / ((wh + height) / 100));
percent = percentage/100;
}
}
return percent;
},
update: function(percent) {
var $this = this,
css = {'transform':''},
compercent = percent * (1 - (this.velocity - (this.velocity * percent))),
opts, val;
if (compercent < 0) compercent = 0;
if (compercent > 1) compercent = 1;
if (this._percent !== undefined && this._percent == compercent) {
return;
}
Object.keys(this.props).forEach(function(prop) {
opts = this.props[prop];
if (percent === 0) {
val = opts.start;
} else if(percent === 1) {
val = opts.end;
} else if(opts.diff !== undefined) {
val = opts.start + (opts.diff * compercent * opts.dir);
}
if ((prop == 'bg' || prop == 'bgp') && !this._bgcover) {
this._bgcover = initBgImageParallax(this, prop, opts);
}
switch(prop) {
// transforms
case "x":
css.transform += supports3d ? ' translate3d('+val+'px, 0, 0)':' translateX('+val+'px)';
break;
case "xp":
css.transform += supports3d ? ' translate3d('+val+'%, 0, 0)':' translateX('+val+'%)';
break;
case "y":
css.transform += supports3d ? ' translate3d(0, '+val+'px, 0)':' translateY('+val+'px)';
break;
case "yp":
css.transform += supports3d ? ' translate3d(0, '+val+'%, 0)':' translateY('+val+'%)';
break;
case "rotate":
css.transform += ' rotate('+val+'deg)';
break;
case "scale":
css.transform += ' scale('+val+')';
break;
// bg image
case "bg":
// don't move if image height is too small
// if ($this.element.data('bgsize') && ($this.element.data('bgsize').h + val - window.innerHeight) < 0) {
// break;
// }
css['background-position'] = '50% '+val+'px';
break;
case "bgp":
css['background-position'] = '50% '+val+'%';
break;
// color
case "color":
case "background-color":
case "border-color":
css[prop] = calcColor(opts.start, opts.end, compercent);
break;
default:
css[prop] = val;
break;
}
}.bind(this));
this.element.css(css);
this._percent = compercent;
},
_getStartValue: function(prop) {
var value = 0;
switch(prop) {
case 'scale':
value = 1;
break;
default:
value = this.element.css(prop);
}
return (value || 0);
}
});
// helper
function initBgImageParallax(obj, prop, opts) {
var img = new Image(), url, element, size, check, ratio, width, height;
element = obj.element.css({'background-size': 'cover', 'background-repeat': 'no-repeat'});
url = element.css('background-image').replace(/^url\(/g, '').replace(/\)$/g, '').replace(/("|')/g, '');
check = function() {
var w = element.innerWidth(), h = element.innerHeight(), extra = (prop=='bg') ? opts.diff : (opts.diff/100) * h;
h += extra;
w += Math.ceil(extra * ratio);
if (w-extra < size.w && h < size.h) {
return obj.element.css({'background-size': 'auto'});
}
// if element height < parent height (gap underneath)
if ((w / ratio) < h) {
width = Math.ceil(h * ratio);
height = h;
if (h > window.innerHeight) {
width = width * 1.2;
height = height * 1.2;
}
// element width < parent width (gap to right)
} else {
width = w;
height = Math.ceil(w / ratio);
}
element.css({'background-size': (width+'px '+height+'px')}).data('bgsize', {w:width,h:height});
};
img.onerror = function(){
// image url doesn't exist
};
img.onload = function(){
size = {w:img.width, h:img.height};
ratio = img.width / img.height;
UI.$win.on("load resize orientationchange", UI.Utils.debounce(function(){
check();
}, 50));
check();
};
img.src = url;
return true;
}
// Some named colors to work with, added by Bradley Ayers
// From Interface by Stefan Petre
// http://interface.eyecon.ro/
var colors = {
'black': [0,0,0,1],
'blue': [0,0,255,1],
'brown': [165,42,42,1],
'cyan': [0,255,255,1],
'fuchsia': [255,0,255,1],
'gold': [255,215,0,1],
'green': [0,128,0,1],
'indigo': [75,0,130,1],
'khaki': [240,230,140,1],
'lime': [0,255,0,1],
'magenta': [255,0,255,1],
'maroon': [128,0,0,1],
'navy': [0,0,128,1],
'olive': [128,128,0,1],
'orange': [255,165,0,1],
'pink': [255,192,203,1],
'purple': [128,0,128,1],
'violet': [128,0,128,1],
'red': [255,0,0,1],
'silver': [192,192,192,1],
'white': [255,255,255,1],
'yellow': [255,255,0,1],
'transparent': [255,255,255,0]
};
function calcColor(start, end, pos) {
start = parseColor(start);
end = parseColor(end);
pos = pos || 0;
return calculateColor(start, end, pos);
}
/**!
* @preserve Color animation 1.6.0
* http://www.bitstorm.org/jquery/color-animation/
* Copyright 2011, 2013 Edwin Martin <edwin@bitstorm.org>
* Released under the MIT and GPL licenses.
*/
// Calculate an in-between color. Returns "#aabbcc"-like string.
function calculateColor(begin, end, pos) {
var color = 'rgba('
+ parseInt((begin[0] + pos * (end[0] - begin[0])), 10) + ','
+ parseInt((begin[1] + pos * (end[1] - begin[1])), 10) + ','
+ parseInt((begin[2] + pos * (end[2] - begin[2])), 10) + ','
+ (begin && end ? parseFloat(begin[3] + pos * (end[3] - begin[3])) : 1);
color += ')';
return color;
}
// Parse an CSS-syntax color. Outputs an array [r, g, b]
function parseColor(color) {
var match, quadruplet;
// Match #aabbcc
if (match = /#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})/.exec(color)) {
quadruplet = [parseInt(match[1], 16), parseInt(match[2], 16), parseInt(match[3], 16), 1];
// Match #abc
} else if (match = /#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])/.exec(color)) {
quadruplet = [parseInt(match[1], 16) * 17, parseInt(match[2], 16) * 17, parseInt(match[3], 16) * 17, 1];
// Match rgb(n, n, n)
} else if (match = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) {
quadruplet = [parseInt(match[1]), parseInt(match[2]), parseInt(match[3]), 1];
} 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)) {
quadruplet = [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10),parseFloat(match[4])];
// No browser returns rgb(n%, n%, n%), so little reason to support this format.
} else {
quadruplet = colors[color] || [255,255,255,0];
}
return quadruplet;
}
return UI.parallax;
});