(function (global, factory){
typeof exports==='object'&&typeof module!=='undefined' ? module.exports=factory() :
typeof define==='function'&&define.amd ? define(factory) :
(global=typeof globalThis!=='undefined' ? globalThis:global||self, global.jarallax=factory());
})(this, (function (){ 'use strict';
function ready(callback){
if(document.readyState==='complete'||document.readyState==='interactive'){
callback();
}else{
document.addEventListener('DOMContentLoaded', callback, {
capture: true,
once: true,
passive: true
});
}}
let win;
if(typeof window!=='undefined'){
win=window;
}else if(typeof global!=='undefined'){
win=global;
}else if(typeof self!=='undefined'){
win=self;
}else{
win={};}
var global$1=win;
var defaults={
type: 'scroll',
speed: 0.5,
containerClass: 'jarallax-container',
imgSrc: null,
imgElement: '.jarallax-img',
imgSize: 'cover',
imgPosition: '50% 50%',
imgRepeat: 'no-repeat',
keepImg: false,
elementInViewport: null,
zIndex: -100,
disableParallax: false,
onScroll: null,
onInit: null,
onDestroy: null,
onCoverImage: null,
videoClass: 'jarallax-video',
videoSrc: null,
videoStartTime: 0,
videoEndTime: 0,
videoVolume: 0,
videoLoop: true,
videoPlayOnlyVisible: true,
videoLazyLoading: true,
disableVideo: false,
onVideoInsert: null,
onVideoWorkerInit: null
};
function css(el, styles){
if(typeof styles==='string'){
return global$1.getComputedStyle(el).getPropertyValue(styles);
}
Object.keys(styles).forEach(key=> {
el.style[key]=styles[key];
});
return el;
}
function extend(out, ...args){
out=out||{};
Object.keys(args).forEach(i=> {
if(!args[i]){
return;
}
Object.keys(args[i]).forEach(key=> {
out[key]=args[i][key];
});
});
return out;
}
function getParents(elem){
const parents=[];
while (elem.parentElement!==null){
elem=elem.parentElement;
if(elem.nodeType===1){
parents.push(elem);
}}
return parents;
}
const {
navigator: navigator$1
}=global$1;
const mobileAgent= /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator$1.userAgent);
function isMobile(){
return mobileAgent;
}
let wndW;
let wndH;
let $deviceHelper;
function getDeviceHeight(){
if(!$deviceHelper&&document.body){
$deviceHelper=document.createElement('div');
$deviceHelper.style.cssText='position: fixed; top: -9999px; left: 0; height: 100vh; width: 0;';
document.body.appendChild($deviceHelper);
}
return ($deviceHelper ? $deviceHelper.clientHeight:0)||global$1.innerHeight||document.documentElement.clientHeight;
}
function updateWindowHeight(){
wndW=global$1.innerWidth||document.documentElement.clientWidth;
if(isMobile()){
wndH=getDeviceHeight();
}else{
wndH=global$1.innerHeight||document.documentElement.clientHeight;
}}
updateWindowHeight();
global$1.addEventListener('resize', updateWindowHeight);
global$1.addEventListener('orientationchange', updateWindowHeight);
global$1.addEventListener('load', updateWindowHeight);
ready(()=> {
updateWindowHeight();
});
function getWindowSize(){
return {
width: wndW,
height: wndH
};}
const jarallaxList=[];
function updateParallax(){
if(!jarallaxList.length){
return;
}
const {
width: wndW,
height: wndH
}=getWindowSize();
jarallaxList.forEach((data, k)=> {
const {
instance,
oldData
}=data;
if(!instance.isVisible()){
return;
}
const clientRect=instance.$item.getBoundingClientRect();
const newData={
width: clientRect.width,
height: clientRect.height,
top: clientRect.top,
bottom: clientRect.bottom,
wndW,
wndH
};
const isResized = !oldData||oldData.wndW!==newData.wndW||oldData.wndH!==newData.wndH||oldData.width!==newData.width||oldData.height!==newData.height;
const isScrolled=isResized||!oldData||oldData.top!==newData.top||oldData.bottom!==newData.bottom;
jarallaxList[k].oldData=newData;
if(isResized){
instance.onResize();
}
if(isScrolled){
instance.onScroll();
}});
global$1.requestAnimationFrame(updateParallax);
}
const visibilityObserver=new global$1.IntersectionObserver(entries=> {
entries.forEach(entry=> {
entry.target.jarallax.isElementInViewport=entry.isIntersecting;
});
}, {
rootMargin: '50px'
});
function addObserver(instance){
jarallaxList.push({
instance
});
if(jarallaxList.length===1){
global$1.requestAnimationFrame(updateParallax);
}
visibilityObserver.observe(instance.options.elementInViewport||instance.$item);
}
function removeObserver(instance){
jarallaxList.forEach((data, key)=> {
if(data.instance.instanceID===instance.instanceID){
jarallaxList.splice(key, 1);
}});
visibilityObserver.unobserve(instance.options.elementInViewport||instance.$item);
}
const {
navigator
}=global$1;
let instanceID=0;
class Jarallax {
constructor(item, userOptions){
const self=this;
self.instanceID=instanceID;
instanceID +=1;
self.$item=item;
self.defaults={
...defaults
};
const dataOptions=self.$item.dataset||{};
const pureDataOptions={};
Object.keys(dataOptions).forEach(key=> {
const lowerCaseOption=key.substr(0, 1).toLowerCase() + key.substr(1);
if(lowerCaseOption&&typeof self.defaults[lowerCaseOption]!=='undefined'){
pureDataOptions[lowerCaseOption]=dataOptions[key];
}});
self.options=self.extend({}, self.defaults, pureDataOptions, userOptions);
self.pureOptions=self.extend({}, self.options);
Object.keys(self.options).forEach(key=> {
if(self.options[key]==='true'){
self.options[key]=true;
}else if(self.options[key]==='false'){
self.options[key]=false;
}});
self.options.speed=Math.min(2, Math.max(-1, parseFloat(self.options.speed)));
if(typeof self.options.disableParallax==='string'){
self.options.disableParallax=new RegExp(self.options.disableParallax);
}
if(self.options.disableParallax instanceof RegExp){
const disableParallaxRegexp=self.options.disableParallax;
self.options.disableParallax=()=> disableParallaxRegexp.test(navigator.userAgent);
}
if(typeof self.options.disableParallax!=='function'){
const disableParallaxDefault=self.options.disableParallax;
self.options.disableParallax=()=> disableParallaxDefault===true;
}
if(typeof self.options.disableVideo==='string'){
self.options.disableVideo=new RegExp(self.options.disableVideo);
}
if(self.options.disableVideo instanceof RegExp){
const disableVideoRegexp=self.options.disableVideo;
self.options.disableVideo=()=> disableVideoRegexp.test(navigator.userAgent);
}
if(typeof self.options.disableVideo!=='function'){
const disableVideoDefault=self.options.disableVideo;
self.options.disableVideo=()=> disableVideoDefault===true;
}
let elementInVP=self.options.elementInViewport;
if(elementInVP&&typeof elementInVP==='object'&&typeof elementInVP.length!=='undefined'){
[elementInVP]=elementInVP;
}
if(!(elementInVP instanceof Element)){
elementInVP=null;
}
self.options.elementInViewport=elementInVP;
self.image={
src: self.options.imgSrc||null,
$container: null,
useImgTag: false,
position: 'fixed'
};
if(self.initImg()&&self.canInitParallax()){
self.init();
}}
css(el, styles){
return css(el, styles);
}
extend(out, ...args){
return extend(out, ...args);
}
getWindowData(){
const {
width,
height
}=getWindowSize();
return {
width,
height,
y: document.documentElement.scrollTop
};}
initImg(){
const self=this;
let $imgElement=self.options.imgElement;
if($imgElement&&typeof $imgElement==='string'){
$imgElement=self.$item.querySelector($imgElement);
}
if(!($imgElement instanceof Element)){
if(self.options.imgSrc){
$imgElement=new Image();
$imgElement.src=self.options.imgSrc;
}else{
$imgElement=null;
}}
if($imgElement){
if(self.options.keepImg){
self.image.$item=$imgElement.cloneNode(true);
}else{
self.image.$item=$imgElement;
self.image.$itemParent=$imgElement.parentNode;
}
self.image.useImgTag=true;
}
if(self.image.$item){
return true;
}
if(self.image.src===null){
self.image.src='data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
self.image.bgImage=self.css(self.$item, 'background-image');
}
return !(!self.image.bgImage||self.image.bgImage==='none');
}
canInitParallax(){
return !this.options.disableParallax();
}
init(){
const self=this;
const containerStyles={
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
overflow: 'hidden'
};
let imageStyles={
pointerEvents: 'none',
transformStyle: 'preserve-3d',
backfaceVisibility: 'hidden'
};
if(!self.options.keepImg){
const curStyle=self.$item.getAttribute('style');
if(curStyle){
self.$item.setAttribute('data-jarallax-original-styles', curStyle);
}
if(self.image.useImgTag){
const curImgStyle=self.image.$item.getAttribute('style');
if(curImgStyle){
self.image.$item.setAttribute('data-jarallax-original-styles', curImgStyle);
}}
}
if(self.css(self.$item, 'position')==='static'){
self.css(self.$item, {
position: 'relative'
});
}
if(self.css(self.$item, 'z-index')==='auto'){
self.css(self.$item, {
zIndex: 0
});
}
self.image.$container=document.createElement('div');
self.css(self.image.$container, containerStyles);
self.css(self.image.$container, {
'z-index': self.options.zIndex
});
if(this.image.position==='fixed'){
self.css(self.image.$container, {
'-webkit-clip-path': 'polygon(0 0, 100% 0, 100% 100%, 0 100%)',
'clip-path': 'polygon(0 0, 100% 0, 100% 100%, 0 100%)'
});
}
self.image.$container.setAttribute('id', `jarallax-container-${self.instanceID}`);
if(self.options.containerClass){
self.image.$container.setAttribute('class', self.options.containerClass);
}
self.$item.appendChild(self.image.$container);
if(self.image.useImgTag){
imageStyles=self.extend({
'object-fit': self.options.imgSize,
'object-position': self.options.imgPosition,
'max-width': 'none'
}, containerStyles, imageStyles);
}else{
self.image.$item=document.createElement('div');
if(self.image.src){
imageStyles=self.extend({
'background-position': self.options.imgPosition,
'background-size': self.options.imgSize,
'background-repeat': self.options.imgRepeat,
'background-image': self.image.bgImage||`url("${self.image.src}")`
}, containerStyles, imageStyles);
}}
if(self.options.type==='opacity'||self.options.type==='scale'||self.options.type==='scale-opacity'||self.options.speed===1){
self.image.position='absolute';
}
if(self.image.position==='fixed'){
const $parents=getParents(self.$item).filter(el=> {
const styles=global$1.getComputedStyle(el);
const parentTransform=styles['-webkit-transform']||styles['-moz-transform']||styles.transform;
const overflowRegex=/(auto|scroll)/;
return parentTransform&&parentTransform!=='none'||overflowRegex.test(styles.overflow + styles['overflow-y'] + styles['overflow-x']);
});
self.image.position=$parents.length ? 'absolute':'fixed';
}
imageStyles.position=self.image.position;
self.css(self.image.$item, imageStyles);
self.image.$container.appendChild(self.image.$item);
self.onResize();
self.onScroll(true);
if(self.options.onInit){
self.options.onInit.call(self);
}
if(self.css(self.$item, 'background-image')!=='none'){
self.css(self.$item, {
'background-image': 'none'
});
}
addObserver(self);
}
destroy(){
const self=this;
removeObserver(self);
const originalStylesTag=self.$item.getAttribute('data-jarallax-original-styles');
self.$item.removeAttribute('data-jarallax-original-styles');
if(!originalStylesTag){
self.$item.removeAttribute('style');
}else{
self.$item.setAttribute('style', originalStylesTag);
}
if(self.image.useImgTag){
const originalStylesImgTag=self.image.$item.getAttribute('data-jarallax-original-styles');
self.image.$item.removeAttribute('data-jarallax-original-styles');
if(!originalStylesImgTag){
self.image.$item.removeAttribute('style');
}else{
self.image.$item.setAttribute('style', originalStylesTag);
}
if(self.image.$itemParent){
self.image.$itemParent.appendChild(self.image.$item);
}}
if(self.image.$container){
self.image.$container.parentNode.removeChild(self.image.$container);
}
if(self.options.onDestroy){
self.options.onDestroy.call(self);
}
delete self.$item.jarallax;
}
coverImage(){
const self=this;
const {
height: wndH
}=getWindowSize();
const rect=self.image.$container.getBoundingClientRect();
const contH=rect.height;
const {
speed
}=self.options;
const isScroll=self.options.type==='scroll'||self.options.type==='scroll-opacity';
let scrollDist=0;
let resultH=contH;
let resultMT=0;
if(isScroll){
if(speed < 0){
scrollDist=speed * Math.max(contH, wndH);
if(wndH < contH){
scrollDist -=speed * (contH - wndH);
}}else{
scrollDist=speed * (contH + wndH);
}
if(speed > 1){
resultH=Math.abs(scrollDist - wndH);
}else if(speed < 0){
resultH=scrollDist / speed + Math.abs(scrollDist);
}else{
resultH +=(wndH - contH) * (1 - speed);
}
scrollDist /=2;
}
self.parallaxScrollDistance=scrollDist;
if(isScroll){
resultMT=(wndH - resultH) / 2;
}else{
resultMT=(contH - resultH) / 2;
}
self.css(self.image.$item, {
height: `${resultH}px`,
marginTop: `${resultMT}px`,
left: self.image.position==='fixed' ? `${rect.left}px`:'0',
width: `${rect.width}px`
});
if(self.options.onCoverImage){
self.options.onCoverImage.call(self);
}
return {
image: {
height: resultH,
marginTop: resultMT
},
container: rect
};}
isVisible(){
return this.isElementInViewport||false;
}
onScroll(force){
const self=this;
if(!force&&!self.isVisible()){
return;
}
const {
height: wndH
}=getWindowSize();
const rect=self.$item.getBoundingClientRect();
const contT=rect.top;
const contH=rect.height;
const styles={};
const beforeTop=Math.max(0, contT);
const beforeTopEnd=Math.max(0, contH + contT);
const afterTop=Math.max(0, -contT);
const beforeBottom=Math.max(0, contT + contH - wndH);
const beforeBottomEnd=Math.max(0, contH - (contT + contH - wndH));
const afterBottom=Math.max(0, -contT + wndH - contH);
const fromViewportCenter=1 - 2 * ((wndH - contT) / (wndH + contH));
let visiblePercent=1;
if(contH < wndH){
visiblePercent=1 - (afterTop||beforeBottom) / contH;
}else if(beforeTopEnd <=wndH){
visiblePercent=beforeTopEnd / wndH;
}else if(beforeBottomEnd <=wndH){
visiblePercent=beforeBottomEnd / wndH;
}
if(self.options.type==='opacity'||self.options.type==='scale-opacity'||self.options.type==='scroll-opacity'){
styles.transform='translate3d(0,0,0)';
styles.opacity=visiblePercent;
}
if(self.options.type==='scale'||self.options.type==='scale-opacity'){
let scale=1;
if(self.options.speed < 0){
scale -=self.options.speed * visiblePercent;
}else{
scale +=self.options.speed * (1 - visiblePercent);
}
styles.transform=`scale(${scale}) translate3d(0,0,0)`;
}
if(self.options.type==='scroll'||self.options.type==='scroll-opacity'){
let positionY=self.parallaxScrollDistance * fromViewportCenter;
if(self.image.position==='absolute'){
positionY -=contT;
}
styles.transform=`translate3d(0,${positionY}px,0)`;
}
self.css(self.image.$item, styles);
if(self.options.onScroll){
self.options.onScroll.call(self, {
section: rect,
beforeTop,
beforeTopEnd,
afterTop,
beforeBottom,
beforeBottomEnd,
afterBottom,
visiblePercent,
fromViewportCenter
});
}}
onResize(){
this.coverImage();
}}
const jarallax=function (items, options, ...args){
if(typeof HTMLElement==='object' ? items instanceof HTMLElement:items&&typeof items==='object'&&items!==null&&items.nodeType===1&&typeof items.nodeName==='string'){
items=[items];
}
const len=items.length;
let k=0;
let ret;
for (k; k < len; k +=1){
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;
};
jarallax.constructor=Jarallax;
const $=global$1.jQuery;
if(typeof $!=='undefined'){
const $Plugin=function (...args){
Array.prototype.unshift.call(args, this);
const res=jarallax.apply(global$1, args);
return typeof res!=='object' ? res:this;
};
$Plugin.constructor=jarallax.constructor;
const old$Plugin=$.fn.jarallax;
$.fn.jarallax=$Plugin;
$.fn.jarallax.noConflict=function (){
$.fn.jarallax=old$Plugin;
return this;
};}
ready(()=> {
jarallax(document.querySelectorAll('[data-jarallax]'));
});
return jarallax;
}));