(function (){
const {
mergeObjects,
onResize,
matchMedia,
prefersReducedMotion,
isBuilder
}=BreakdanceFrontend.utils;
class BreakdanceEntrance {
enabledClass='breakdance-animation-enabled';
beforeClass='is-before';
animatingClass='is-animating';
completedClass='is-animated';
defaultOptions={
animation_type: null,
duration: { number: 500, unit: 'ms', style: '500ms' },
delay: { number: 0, unit: 'ms', style: '0ms' },
advanced: {
distance: { number: 100, unit: "px", style: "100px" },
offset: { number: 0, unit: 'px', style: '0px' },
ease: 'power1.out',
anchorPlacement: 'top bottom',
once: false,
disable_at: null
}};
delay=0;
initialized=false;
constructor(selector, options){
gsap.registerPlugin(ScrollTrigger);
this.cleanup=this.cleanup.bind(this);
this.reset=this.reset.bind(this);
this.selector=selector;
this.options=mergeObjects(this.defaultOptions, options);
this.rootEl=document.documentElement;
this.init();
}
getAnimations(){
const distance=this.options.advanced.distance.style;
const unit=this.options.advanced.distance.unit;
return {
fade: [
{ autoAlpha: 0 },
{ autoAlpha: 1 }
],
slideUp: [
{ y: distance },
{ y: `0${unit}` }
],
slideDown: [
{ y: `-=${distance}` },
{ y: `0${unit}` }
],
slideLeft: [
{ x: `-=${distance}` },
{ x: `0${unit}` }
],
slideRight: [
{ x: distance },
{ x: `0${unit}` }
],
flipUp: [
{ perspective: 2500, rotateX: `-=${distance}` },
{ rotateX: `0${unit}` }
],
flipDown: [
{ perspective: 2500, rotateX: distance },
{ rotateX: `0${unit}` }
],
flipLeft: [
{ perspective: 2500, rotateY: `-=${distance}` },
{ rotateY: `0${unit}` }
],
flipRight: [
{ perspective: 2500, rotateY: distance },
{ rotateY: `0${unit}` }
],
zoomIn: [
{ scale: 0.6 },
{ scale: 1 }
],
zoomOut: [
{ scale: 1.2 },
{ scale: 1 }
],
};}
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;
}
cleanup(){
gsap.set(this.element, { clearProps: 'all' });
}
reset(element){
const el=element||this.element;
el.classList.add(this.beforeClass);
el.classList.remove(this.completedClass);
el.classList.remove(this.animatingClass);
}
getOffset(offset, anchor){
const defaultOffset=120;
if(anchor==='top bottom'&&offset.number===0){
return defaultOffset;
}
return offset.number;
}
createTween(){
const type=this.options.animation_type;
const animations=this.getAnimations();
if(!animations[type]){
console.error(`[ENTRANCE] The selected ${type} animation is invalid.`);
return;
}
const [from, to]=animations[type];
const { ease, once, anchorPlacement }=this.options.advanced;
const duration=this.getDuration(this.options.duration);
const delay=this.getDuration(this.options.delay) + this.delay;
const offset=this.getOffset(this.options.advanced.offset, anchorPlacement);
const [elementStart, viewportStart]=anchorPlacement.split(' ');
const anim=gsap.timeline({
delay,
paused: true
});
this.element.classList.add(this.beforeClass);
if(isBuilder()){
this.hideEl();
}
this.startTrigger=ScrollTrigger.create({
trigger: this.element,
start: `${elementStart}+=${offset} ${viewportStart}`,
toggleActions: "play none none none",
once,
onEnter: ()=> anim.play()
});
anim.fromTo(this.element, {
...from,
autoAlpha: 0,
},
{
...to,
duration,
delay,
ease,
autoAlpha: 1,
clearProps: 'all',
immediateRender: false,
onStart: ()=> {
this.element.classList.add(this.animatingClass);
this.element.classList.remove(this.beforeClass);
},
onComplete: ()=> {
this.element.classList.remove(this.animatingClass);
this.element.classList.add(this.completedClass);
},
onReverseComplete: ()=> {
this.element.classList.add(this.beforeClass);
this.element.classList.remove(this.animatingClass);
},
});
if(!once){
this.goToBeginningOnReverse(anim);
}
return anim;
}
goToBeginningOnReverse(animation){
this.endTrigger=ScrollTrigger.create({
trigger: this.element,
start: `top-=10 bottom`,
onLeaveBack: ()=> {
animation.pause(0);
this.cleanup();
if(isBuilder()){
this.hideEl();
}
this.reset();
}});
}
hideEl(){
gsap.set(this.element, { autoAlpha: 0 });
}
update(options){
this.options=mergeObjects(this.defaultOptions, options);
this.destroy();
this.init();
}
replay(delay=0){
this.destroy();
this.reset();
this.delay=delay;
this.init();
}
destroy(){
this.initialized=false;
if(!this.element) return;
this.element.classList.add(this.completedClass);
if(!this.tween) return;
this.tween.kill();
this.tween=null;
this.startTrigger?.kill();
this.endTrigger?.kill();
gsap.set(this.element, { clearProps: 'all' });
}
refresh(){
ScrollTrigger.refresh();
}
initTween(){
if(this.initialized) return;
this.initialized=true;
this.element.classList.remove(this.completedClass);
this.tween=this.createTween();
}
initOrDestroy(){
if(this.canAnimate()){
this.initTween();
}else{
this.destroy();
}}
init(){
if(!this.options.animation_type) return;
this.element=document.querySelector(this.selector);
this.element.bdAnim=this;
if(prefersReducedMotion()){
this.element.classList.add(this.completedClass);
return;
}
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);
});
addEventListener("breakdance_play_animations", (event)=> {
const target=event.target===window ? document.body:event.target;
const nodes=[
target,
...target.querySelectorAll("[data-entrance]")
];
nodes.forEach((el)=> el.bdAnim?.replay(event?.detail?.delay));
});
addEventListener("breakdance_refresh_animations", (event)=> {
const target=event.target===window ? document.body:event.target;
const nodes=[
target,
...target.querySelectorAll("[data-entrance]")
];
nodes.forEach((el)=> el.bdAnim?.cleanup());
});
addEventListener("breakdance_reset_animations", (event)=> {
const target=event.target===window ? document.body:event.target;
const nodes=[
target,
...target.querySelectorAll("[data-entrance]")
];
nodes.forEach((el)=> el.bdAnim?.reset(el));
});
}
static dontLetScrollTriggerMutateScrollPosition(){
window.addEventListener("load", ()=> {
if(!location.hash) return;
if(window.bdeAnimationScrolled) return;
const scrollElem=document.querySelector(location.hash);
if(!scrollElem) return;
requestAnimationFrame(()=> {
scrollElem.scrollIntoView({
behavior: "smooth",
});
});
window.bdeAnimationScrolled=true;
});
}}
window.BreakdanceEntrance=BreakdanceEntrance;
BreakdanceEntrance.autoload();
BreakdanceEntrance.dontLetScrollTriggerMutateScrollPosition();
}());