123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695 |
- /*!
- * Name : Just Another Parallax [Jarallax]
- * Version : 1.8.0
- * Author : _nK https://nkdev.info
- * GitHub : https://github.com/nk-o/jarallax
- */
- (function (window) {
- 'use strict';
- // Adapted from https://gist.github.com/paulirish/1579671
- if(!Date.now) {
- Date.now = function () { return new Date().getTime(); };
- }
- if(!window.requestAnimationFrame) {
- (function () {
- var vendors = ['webkit', 'moz'];
- for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
- var vp = vendors[i];
- window.requestAnimationFrame = window[vp+'RequestAnimationFrame'];
- window.cancelAnimationFrame = window[vp+'CancelAnimationFrame']
- || window[vp+'CancelRequestAnimationFrame'];
- }
- if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy
- || !window.requestAnimationFrame || !window.cancelAnimationFrame) {
- var lastTime = 0;
- window.requestAnimationFrame = function (callback) {
- var now = Date.now();
- var nextTime = Math.max(lastTime + 16, now);
- return setTimeout(function () { callback(lastTime = nextTime); },
- nextTime - now);
- };
- window.cancelAnimationFrame = clearTimeout;
- }
- }());
- }
- // test if css property supported by browser
- // like "transform"
- var tempDiv = document.createElement('div');
- function isPropertySupported (property) {
- var prefixes = ['O','Moz','ms','Ms','Webkit'];
- var i = prefixes.length;
- if (tempDiv.style[property] !== undefined) {
- return true;
- }
- property = property.charAt(0).toUpperCase() + property.substr(1);
- while (--i > -1 && tempDiv.style[prefixes[i] + property] === undefined) { }
- return i >= 0;
- }
- var supportTransform = isPropertySupported('transform');
- var supportTransform3D = isPropertySupported('perspective');
- var ua = navigator.userAgent;
- var isAndroid = ua.toLowerCase().indexOf('android') > -1;
- var isIOs = /iPad|iPhone|iPod/.test(ua) && !window.MSStream;
- var isFirefox = ua.toLowerCase().indexOf('firefox') > -1;
- var isIE = ua.indexOf('MSIE ') > -1 // IE 10 or older
- || ua.indexOf('Trident/') > -1 // IE 11
- || ua.indexOf('Edge/') > -1; // Edge
- var isIElt10 = document.all && !window.atob; // IE 9 or older
- var wndW;
- var wndH;
- function updateWndVars () {
- wndW = window.innerWidth || document.documentElement.clientWidth;
- wndH = window.innerHeight || document.documentElement.clientHeight;
- }
- updateWndVars();
- // list with all jarallax instances
- // need to render all in one scroll/resize event
- var jarallaxList = [];
- // Jarallax instance
- var Jarallax = (function () {
- var instanceID = 0;
- function Jarallax_inner (item, userOptions) {
- var _this = this,
- dataOptions;
- _this.$item = item;
- _this.defaults = {
- type : 'scroll', // type of parallax: scroll, scale, opacity, scale-opacity, scroll-opacity
- speed : 0.5, // supported value from -1 to 2
- imgSrc : null,
- imgWidth : null,
- imgHeight : null,
- elementInViewport : null,
- zIndex : -100,
- noAndroid : false,
- noIos : true,
- // events
- onScroll : null, // function(calculations) {}
- onInit : null, // function() {}
- onDestroy : null, // function() {}
- onCoverImage : null // function() {}
- };
- dataOptions = JSON.parse(_this.$item.getAttribute('data-jarallax') || '{}');
- _this.options = _this.extend({}, _this.defaults, dataOptions, userOptions);
- // stop init if android or ios
- if(!supportTransform || isAndroid && _this.options.noAndroid || isIOs && _this.options.noIos) {
- return;
- }
- // fix speed option [-1.0, 2.0]
- _this.options.speed = Math.min(2, Math.max(-1, parseFloat(_this.options.speed)));
- // custom element to check if parallax in viewport
- var elementInVP = _this.options.elementInViewport;
- // get first item from array
- if(elementInVP && typeof elementInVP === 'object' && typeof elementInVP.length !== 'undefined') {
- elementInVP = elementInVP[0];
- }
- // check if dom element
- if(!elementInVP instanceof Element) {
- elementInVP = null;
- }
- _this.options.elementInViewport = elementInVP;
- _this.instanceID = instanceID++;
- _this.image = {
- src : _this.options.imgSrc || null,
- $container : null,
- $item : null,
- width : _this.options.imgWidth || null,
- height : _this.options.imgHeight || null,
- // fix for some devices
- // use <img> instead of background image - more smoothly
- useImgTag : isIOs || isAndroid || isIE,
- // position absolute is needed on IE9 and FireFox because fixed position have glitches
- position : !supportTransform3D || isFirefox ? 'absolute' : 'fixed'
- };
- if(_this.initImg()) {
- _this.init();
- }
- }
- return Jarallax_inner;
- }());
- // add styles to element
- Jarallax.prototype.css = function (el, styles) {
- if(typeof styles === 'string') {
- if(window.getComputedStyle) {
- return window.getComputedStyle(el).getPropertyValue(styles);
- }
- return el.style[styles];
- }
- // add transform property with vendor prefixes
- if(styles.transform) {
- if (supportTransform3D) {
- styles.transform += ' translateZ(0)';
- }
- styles.WebkitTransform = styles.MozTransform = styles.msTransform = styles.OTransform = styles.transform;
- }
- for(var k in styles) {
- el.style[k] = styles[k];
- }
- return el;
- };
- // Extend like jQuery.extend
- Jarallax.prototype.extend = function (out) {
- out = out || {};
- for (var i = 1; i < arguments.length; i++) {
- if (!arguments[i]) {
- continue;
- }
- for (var key in arguments[i]) {
- if (arguments[i].hasOwnProperty(key)) {
- out[key] = arguments[i][key];
- }
- }
- }
- return out;
- };
- // Jarallax functions
- Jarallax.prototype.initImg = function () {
- var _this = this;
- // get image src
- if(_this.image.src === null) {
- _this.image.src = _this.css(_this.$item, 'background-image').replace(/^url\(['"]?/g,'').replace(/['"]?\)$/g,'');
- }
- return !(!_this.image.src || _this.image.src === 'none');
- };
- Jarallax.prototype.init = function () {
- var _this = this,
- containerStyles = {
- position : 'absolute',
- top : 0,
- left : 0,
- width : '100%',
- height : '100%',
- overflow : 'hidden',
- pointerEvents : 'none'
- },
- imageStyles = {};
- // save default user styles
- _this.$item.setAttribute('data-jarallax-original-styles', _this.$item.getAttribute('style'));
- // set relative position and z-index to the parent
- if (_this.css(_this.$item, 'position') === 'static') {
- _this.css(_this.$item, {
- position: 'relative'
- });
- }
- if (_this.css(_this.$item, 'z-index') === 'auto') {
- _this.css(_this.$item, {
- zIndex: 0
- });
- }
- // container for parallax image
- _this.image.$container = document.createElement('div');
- _this.css(_this.image.$container, containerStyles);
- _this.css(_this.image.$container, {
- visibility : 'hidden',
- 'z-index' : _this.options.zIndex
- });
- _this.image.$container.setAttribute('id', 'jarallax-container-' + _this.instanceID);
- _this.$item.appendChild(_this.image.$container);
- // use img tag
- if(_this.image.useImgTag) {
- _this.image.$item = document.createElement('img');
- _this.image.$item.setAttribute('src', _this.image.src);
- imageStyles = _this.extend({
- 'max-width' : 'none'
- }, containerStyles, imageStyles);
- }
- // use div with background image
- else {
- _this.image.$item = document.createElement('div');
- imageStyles = _this.extend({
- 'background-position' : '50% 50%',
- 'background-size' : '100% auto',
- 'background-repeat' : 'no-repeat no-repeat',
- 'background-image' : 'url("' + _this.image.src + '")'
- }, containerStyles, imageStyles);
- }
- // check if one of parents have transform style (without this check, scroll transform will be inverted)
- // discussion - https://github.com/nk-o/jarallax/issues/9
- var parentWithTransform = 0;
- var $itemParents = _this.$item;
- while ($itemParents !== null && $itemParents !== document && parentWithTransform === 0) {
- var parent_transform = _this.css($itemParents, '-webkit-transform') || _this.css($itemParents, '-moz-transform') || _this.css($itemParents, 'transform');
- if(parent_transform && parent_transform !== 'none') {
- parentWithTransform = 1;
- // add transform on parallax container if there is parent with transform
- _this.css(_this.image.$container, {
- transform: 'translateX(0) translateY(0)'
- });
- }
- $itemParents = $itemParents.parentNode;
- }
- // absolute position if one of parents have transformations or parallax without scroll
- if (parentWithTransform || _this.options.type === 'opacity'|| _this.options.type === 'scale' || _this.options.type === 'scale-opacity') {
- _this.image.position = 'absolute';
- }
- // add position to parallax block
- imageStyles.position = _this.image.position;
- // parallax image
- _this.css(_this.image.$item, imageStyles);
- _this.image.$container.appendChild(_this.image.$item);
- // cover image if width and height is ready
- function initAfterReady () {
- _this.coverImage();
- _this.clipContainer();
- _this.onScroll(true);
- // call onInit event
- if(_this.options.onInit) {
- _this.options.onInit.call(_this);
- }
- // timeout to fix IE blinking
- setTimeout(function () {
- if(_this.$item) {
- // remove default user background
- _this.css(_this.$item, {
- 'background-image' : 'none',
- 'background-attachment' : 'scroll',
- 'background-size' : 'auto'
- });
- }
- }, 0);
- }
- if(_this.image.width && _this.image.height) {
- // init if width and height already exists
- initAfterReady();
- } else {
- // load image and get width and height
- _this.getImageSize(_this.image.src, function (width, height) {
- _this.image.width = width;
- _this.image.height = height;
- initAfterReady();
- });
- }
- jarallaxList.push(_this);
- };
- Jarallax.prototype.destroy = function () {
- var _this = this;
- // remove from instances list
- for(var k = 0, len = jarallaxList.length; k < len; k++) {
- if(jarallaxList[k].instanceID === _this.instanceID) {
- jarallaxList.splice(k, 1);
- break;
- }
- }
- // return styles on container as before jarallax init
- var originalStylesTag = _this.$item.getAttribute('data-jarallax-original-styles');
- _this.$item.removeAttribute('data-jarallax-original-styles');
- // null occurs if there is no style tag before jarallax init
- if(originalStylesTag === 'null') {
- _this.$item.removeAttribute('style');
- } else {
- _this.$item.setAttribute('style', originalStylesTag);
- }
- // remove additional dom elements
- if(_this.$clipStyles) {
- _this.$clipStyles.parentNode.removeChild(_this.$clipStyles);
- }
- _this.image.$container.parentNode.removeChild(_this.image.$container);
- // call onDestroy event
- if(_this.options.onDestroy) {
- _this.options.onDestroy.call(_this);
- }
- // delete jarallax from item
- delete _this.$item.jarallax;
- // delete all variables
- for(var n in _this) {
- delete _this[n];
- }
- };
- Jarallax.prototype.getImageSize = function (src, callback) {
- if(!src || !callback) {
- return;
- }
- var tempImg = new Image();
- tempImg.onload = function () {
- callback(tempImg.width, tempImg.height);
- };
- tempImg.src = src;
- };
- // it will remove some image overlapping
- // overlapping occur due to an image position fixed inside absolute position element (webkit based browsers works without any fix)
- Jarallax.prototype.clipContainer = function () {
- // clip is not working properly on real IE9 and less
- if(isIElt10) {
- return;
- }
- var _this = this,
- rect = _this.image.$container.getBoundingClientRect(),
- width = rect.width,
- height = rect.height;
- if(!_this.$clipStyles) {
- _this.$clipStyles = document.createElement('style');
- _this.$clipStyles.setAttribute('type', 'text/css');
- _this.$clipStyles.setAttribute('id', '#jarallax-clip-' + _this.instanceID);
- var head = document.head || document.getElementsByTagName('head')[0];
- head.appendChild(_this.$clipStyles);
- }
- var styles = [
- '#jarallax-container-' + _this.instanceID + ' {',
- ' clip: rect(0 ' + width + 'px ' + height + 'px 0);',
- ' clip: rect(0, ' + width + 'px, ' + height + 'px, 0);',
- '}'
- ].join('\n');
- // add clip styles inline (this method need for support IE8 and less browsers)
- if (_this.$clipStyles.styleSheet){
- _this.$clipStyles.styleSheet.cssText = styles;
- } else {
- _this.$clipStyles.innerHTML = styles;
- }
- };
- Jarallax.prototype.coverImage = function () {
- var _this = this;
- if(!_this.image.width || !_this.image.height) {
- return;
- }
- var rect = _this.image.$container.getBoundingClientRect(),
- contW = rect.width,
- contH = rect.height,
- contL = rect.left,
- imgW = _this.image.width,
- imgH = _this.image.height,
- speed = _this.options.speed,
- isScroll = _this.options.type === 'scroll' || _this.options.type === 'scroll-opacity',
- scrollDist = 0,
- resultW = 0,
- resultH = contH,
- resultML = 0,
- resultMT = 0;
- // scroll parallax
- if(isScroll) {
- // scroll distance and height for image
- if (speed < 0) {
- scrollDist = speed * Math.max(contH, wndH);
- } else {
- scrollDist = speed * (contH + wndH);
- }
- // size for scroll parallax
- if (speed > 1) {
- resultH = Math.abs(scrollDist - wndH);
- } else if (speed < 0) {
- resultH = scrollDist / speed + Math.abs(scrollDist);
- } else {
- resultH += Math.abs(wndH - contH) * (1 - speed);
- }
- scrollDist /= 2;
- }
- // calculate width relative to height and image size
- resultW = resultH * imgW / imgH;
- if(resultW < contW) {
- resultW = contW;
- resultH = resultW * imgH / imgW;
- }
- // center parallax image
- if(isScroll) {
- resultML = contL + (contW - resultW) / 2;
- resultMT = (wndH - resultH) / 2;
- } else {
- resultML = (contW - resultW) / 2;
- resultMT = (contH - resultH) / 2;
- }
- // fix if parallax block in absolute position
- if(_this.image.position === 'absolute') {
- resultML -= contL;
- }
- // store scroll distance
- _this.parallaxScrollDistance = scrollDist;
- // apply result to item
- _this.css(_this.image.$item, {
- width: resultW + 'px',
- height: resultH + 'px',
- marginLeft: resultML + 'px',
- marginTop: resultMT + 'px'
- });
- // call onCoverImage event
- if(_this.options.onCoverImage) {
- _this.options.onCoverImage.call(_this);
- }
- };
- Jarallax.prototype.isVisible = function () {
- return this.isElementInViewport || false;
- };
- Jarallax.prototype.onScroll = function (force) {
- var _this = this;
- if(!_this.image.width || !_this.image.height) {
- return;
- }
- var rect = _this.$item.getBoundingClientRect(),
- contT = rect.top,
- contH = rect.height,
- styles = {
- visibility : 'visible',
- backgroundPosition : '50% 50%'
- };
- // check if in viewport
- var viewportRect = rect;
- if(_this.options.elementInViewport) {
- viewportRect = _this.options.elementInViewport.getBoundingClientRect();
- }
- _this.isElementInViewport =
- viewportRect.bottom >= 0 &&
- viewportRect.right >= 0 &&
- viewportRect.top <= wndH &&
- viewportRect.left <= wndW;
- // stop calculations if item is not in viewport
- if (force ? false : !_this.isElementInViewport) {
- return;
- }
- // calculate parallax helping variables
- var beforeTop = Math.max(0, contT),
- beforeTopEnd = Math.max(0, contH + contT),
- afterTop = Math.max(0, -contT),
- beforeBottom = Math.max(0, contT + contH - wndH),
- beforeBottomEnd = Math.max(0, contH - (contT + contH - wndH)),
- afterBottom = Math.max(0, -contT + wndH - contH),
- fromViewportCenter = 1 - 2 * (wndH - contT) / (wndH + contH);
- // calculate on how percent of section is visible
- var visiblePercent = 1;
- if(contH < wndH) {
- visiblePercent = 1 - (afterTop || beforeBottom) / contH;
- } else {
- if(beforeTopEnd <= wndH) {
- visiblePercent = beforeTopEnd / wndH;
- } else if (beforeBottomEnd <= wndH) {
- visiblePercent = beforeBottomEnd / wndH;
- }
- }
- // opacity
- if(_this.options.type === 'opacity' || _this.options.type === 'scale-opacity' || _this.options.type === 'scroll-opacity') {
- styles.transform = ''; // empty to add translateZ(0) where it is possible
- styles.opacity = visiblePercent;
- }
- // scale
- if(_this.options.type === 'scale' || _this.options.type === 'scale-opacity') {
- var scale = 1;
- if(_this.options.speed < 0) {
- scale -= _this.options.speed * visiblePercent;
- } else {
- scale += _this.options.speed * (1 - visiblePercent);
- }
- styles.transform = 'scale(' + scale + ')';
- }
- // scroll
- if(_this.options.type === 'scroll' || _this.options.type === 'scroll-opacity') {
- var positionY = _this.parallaxScrollDistance * fromViewportCenter;
- // fix if parallax block in absolute position
- if(_this.image.position === 'absolute') {
- positionY -= contT;
- }
- styles.transform = 'translateY(' + positionY + 'px)';
- }
- _this.css(_this.image.$item, styles);
- // call onScroll event
- if(_this.options.onScroll) {
- _this.options.onScroll.call(_this, {
- section: rect,
- beforeTop: beforeTop,
- beforeTopEnd: beforeTopEnd,
- afterTop: afterTop,
- beforeBottom: beforeBottom,
- beforeBottomEnd: beforeBottomEnd,
- afterBottom: afterBottom,
- visiblePercent: visiblePercent,
- fromViewportCenter: fromViewportCenter
- });
- }
- };
- // init events
- function addEventListener (el, eventName, handler) {
- if (el.addEventListener) {
- el.addEventListener(eventName, handler);
- } else {
- el.attachEvent('on' + eventName, function (){
- handler.call(el);
- });
- }
- }
- function update (e) {
- window.requestAnimationFrame(function () {
- if(e.type !== 'scroll') {
- updateWndVars();
- }
- for(var k = 0, len = jarallaxList.length; k < len; k++) {
- // cover image and clip needed only when parallax container was changed
- if(e.type !== 'scroll') {
- jarallaxList[k].coverImage();
- jarallaxList[k].clipContainer();
- }
- jarallaxList[k].onScroll();
- }
- });
- }
- addEventListener(window, 'scroll', update);
- addEventListener(window, 'resize', update);
- addEventListener(window, 'orientationchange', update);
- addEventListener(window, 'load', update);
- // global definition
- var plugin = function (items) {
- // check for dom element
- // thanks: http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
- if(typeof HTMLElement === "object" ? items instanceof HTMLElement : items && typeof items === "object" && items !== null && items.nodeType === 1 && typeof items.nodeName==="string") {
- items = [items];
- }
- var options = arguments[1],
- args = Array.prototype.slice.call(arguments, 2),
- len = items.length,
- k = 0,
- ret;
- for (k; k < len; k++) {
- if (typeof options === 'object' || typeof options === 'undefined') {
- if(!items[k].jarallax) {
- items[k].jarallax = new Jarallax(items[k], options);
- }
- }
- else if(items[k].jarallax) {
- ret = items[k].jarallax[options].apply(items[k].jarallax, args);
- }
- if (typeof ret !== 'undefined') {
- return ret;
- }
- }
- return items;
- };
- plugin.constructor = Jarallax;
- // no conflict
- var oldPlugin = window.jarallax;
- window.jarallax = plugin;
- window.jarallax.noConflict = function () {
- window.jarallax = oldPlugin;
- return this;
- };
- // jQuery support
- if(typeof jQuery !== 'undefined') {
- var jQueryPlugin = function () {
- var args = arguments || [];
- Array.prototype.unshift.call(args, this);
- var res = plugin.apply(window, args);
- return typeof res !== 'object' ? res : this;
- };
- jQueryPlugin.constructor = Jarallax;
- // no conflict
- var oldJqPlugin = jQuery.fn.jarallax;
- jQuery.fn.jarallax = jQueryPlugin;
- jQuery.fn.jarallax.noConflict = function () {
- jQuery.fn.jarallax = oldJqPlugin;
- return this;
- };
- }
- // data-jarallax initialization
- addEventListener(window, 'DOMContentLoaded', function () {
- plugin(document.querySelectorAll('[data-jarallax], [data-jarallax-video]'));
- });
- }(window));
|