/*! 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; });