(function (){
const {
is,
matchMedia,
toArray,
debounce,
onResize,
mergeObjects,
prefersReducedMotion
}=BreakdanceFrontend.utils;
class BreakdanceParallax {
tweens=[];
enabledClass='breakdance-animation-enabled';
animatingClass='is-animating';
defaultOptions={
enabled: false,
opacity: null,
rotation: null,
scale: null,
x: null,
y: null,
blur: null,
background_color: null,
advanced: {
ease: 'linear',
disable_at: null,
relative_to: 'viewport',
origin: null,
debug: false
}}
initialized=false;
constructor(selector, options){
gsap.registerPlugin(ScrollTrigger);
this.createTween=this.createTween.bind(this);
this.update=debounce(this._update, 100);
this.selector=selector;
this.options=mergeObjects(this.defaultOptions, options);
this.rootEl=document.documentElement;
this.init();
}
parseValue(value){
if(!value) return value;
if(typeof value==='string'&&value.startsWith('var(')){
const cssVar=value.replace(/var\(([^)]+)\)/, '$1');
return getComputedStyle(this.element).getPropertyValue(cssVar);
}
return typeof value==='object' ? value.style:value;
}
parseAnchors(trigger){
const [vBottom, vTop]=trigger||[0, 100];
return {
bottom: 100 - vBottom,
top: 100 - vTop
}}
getTrigger(relativeTo='viewport', selector){
if(relativeTo==='page'){
return document.documentElement;
}else if(relativeTo==='custom'&&selector){
try {
return document.querySelector(selector);
} catch (e){}}
return this.element;
}
getScrollTriggerObject(values){
const { bottom, top }=this.parseAnchors(values.trigger);
const { debug, scrub, relative_to, relative_selector }=this.options.advanced;
const speed=this.getDuration(scrub)||true;
let delayedCall;
const trigger=this.getTrigger(relative_to, relative_selector);
return {
trigger: trigger,
start: `top ${bottom}%`,
end: `bottom ${top}%`,
scrub: speed,
markers: debug,
toggleClass: 'is-parallax-active',
onUpdate: (self)=> {
if(self.getVelocity()===0) return;
this.element.classList.add(this.animatingClass);
if(delayedCall) delayedCall.kill();
delayedCall=gsap.delayedCall(0.3, ()=> {
this.element.classList.remove(this.animatingClass);
});
}};}
createTween([prop, values]){
const cssProp=prop.replace(/_/g, '-');
const ease=this.options.advanced.ease;
const scrollTrigger=this.getScrollTriggerObject(values);
const startValue=this.parseValue(values.start);
const middleValue=this.parseValue(values.middle);
const endValue=this.parseValue(values.end);
const from={ [cssProp]: startValue, ease };
const middle={ [cssProp]: middleValue, ease };
const to={ [cssProp]: endValue, ease };
const tl=gsap.timeline({ scrollTrigger });
const startEndSteps = !is.nil(startValue)&&!is.nil(endValue);
const allSteps=startEndSteps&&!is.nil(middleValue);
if(allSteps){
tl.fromTo(this.element, from, middle);
tl.to(this.element, to);
}else if(startEndSteps){
tl.fromTo(this.element, from, to);
}else if(!is.nil(startValue)){
tl.from(this.element, from);
}else{
tl.to(this.element, to);
}
return tl;
}
canAnimate(){
const breakpoint=this.options.advanced.disable_at;
if(!breakpoint) return true;
return !matchMedia(breakpoint);
}
getDuration(value){
if(!value) return value;
if(value.unit==='s') return value.number;
return value.number / 1000;
}
initTweens(){
if(this.initialized) return;
this.initialized=true;
const { origin }=this.options.advanced;
if(origin){
gsap.set(this.element, {
transformOrigin: `${origin.x}% ${origin.y}%`
});
}
this.tweens=Object.entries(this.options)
.filter(([, obj])=> !is.nil(obj?.start)||!is.nil(obj?.end))
.map(this.createTween);
}
removeDebugMarkers(){
const markers=[
'start',
'end',
'scroller-start',
'scroller-end'
];
markers.forEach((suffix)=> {
const className=`.gsap-marker-${suffix}`;
toArray(className).forEach((elem)=> elem.remove());
});
}
_update(options){
this.options=mergeObjects(this.defaultOptions, options);
this.destroy();
this.init();
}
destroyTweens(){
if(!this.element) return;
if(!this.tweens) return;
this.tweens.forEach((tween)=> {
tween.kill();
tween.scrollTrigger?.kill();
});
this.tweens=[];
gsap.set(this.element, {
clearProps: 'all'
});
this.initialized=false;
}
refresh(){
ScrollTrigger.refresh(true);
}
destroy(){
this.destroyTweens();
this.removeDebugMarkers();
}
initOrDestroy(){
if(this.canAnimate()){
this.initTweens();
}else{
this.destroyTweens();
}}
init(){
if(!this.options.enabled) return;
if(prefersReducedMotion()) return;
this.element=document.querySelector(this.selector);
onResize(()=> this.initOrDestroy());
this.rootEl.classList.add(this.enabledClass);
}
static autoload(){
const loaded=imagesLoaded(document.body);
loaded.on('always', ()=> {
const event=new Event("breakdance_refresh_animations", { bubbles: true });
document.dispatchEvent(event);
ScrollTrigger.refresh(true);
});
}}
window.BreakdanceParallax=BreakdanceParallax;
BreakdanceParallax.autoload();
}());