PageSourceSearch

https://www.ottavy-avocat-montpellier.fr/wp-content/plugins/breakd…/entrance/js/entrance.js?bd_ver=2.8.3

js ottavy-avocat-montpellier.fr collected 2026-10-07 14:21:39 UTC 9,694 bytes, 373 lines download raw bytes

1/* global gsap, ScrollTrigger, imagesLoaded */
2(function () {
3  const {
4    mergeObjects,
5    onResize,
6    matchMedia,
7    prefersReducedMotion,
8    isBuilder
9  } = BreakdanceFrontend.utils;
10
11  class BreakdanceEntrance {
12    enabledClass = 'breakdance-animation-enabled';
13    beforeClass = 'is-before';
14    animatingClass = 'is-animating';
15    completedClass = 'is-animated';
16
17    defaultOptions = {
18      animation_type: null,
19      duration: { number: 500, unit: 'ms', style: '500ms' },
20      delay: { number: 0, unit: 'ms', style: '0ms' },
21      advanced: {
22        distance: { number: 100, unit: "px", style: "100px" },
23        offset: { number: 0, unit: 'px', style: '0px' },
24        ease: 'power1.out',
25        anchorPlacement: 'top bottom',
26        once: false,
27        disable_at: null
28      }
29    };
30
31    delay = 0;
32    initialized = false;
33
34    constructor(selector, options) {
35      gsap.registerPlugin(ScrollTrigger);
36
37      this.cleanup = this.cleanup.bind(this);
38      this.reset = this.reset.bind(this);
39
40      this.selector = selector;
41      this.options = mergeObjects(this.defaultOptions, options);
42      this.rootEl = document.documentElement;
43
44      this.init();
45    }
46
47    getAnimations() {
48      const distance = this.options.advanced.distance.style;
49      const unit = this.options.advanced.distance.unit;
50
51      return {
52        fade: [
53          { autoAlpha: 0 },
54          { autoAlpha: 1 }
55        ],
56
57        slideUp: [
58          { y: distance },
59          { y: `0${unit}` }
60        ],
61        slideDown: [
62          { y: `-=${distance}` },
63          { y: `0${unit}` }
64        ],
65        slideLeft: [
66          { x: `-=${distance}` },
67          { x: `0${unit}` }
68        ],
69        slideRight: [
70          { x: distance },
71          { x: `0${unit}` }
72        ],
73
74        flipUp: [
75          { perspective: 2500, rotateX: `-=${distance}` },
76          { rotateX: `0${unit}` }
77        ],
78
79        flipDown: [
80          { perspective: 2500, rotateX: distance },
81          { rotateX: `0${unit}` }
82        ],
83        flipLeft: [
84          { perspective: 2500, rotateY: `-=${distance}` },
85          { rotateY: `0${unit}` }
86        ],
87        flipRight: [
88          { perspective: 2500, rotateY: distance },
89          { rotateY: `0${unit}` }
90        ],
91
92        zoomIn: [
93          { scale: 0.6 },
94          { scale: 1 }
95        ],
96        zoomOut: [
97          { scale: 1.2 },
98          { scale: 1 }
99        ],
100      };
101    }
102
103    canAnimate() {
104      const breakpoint = this.options.advanced.disable_at;
105      if (!breakpoint) return true;
106      return !matchMedia(breakpoint);
107    }
108
109    getDuration(value) {
110      if (!value) return value;
111      if (value.unit === 's') return value.number;
112      return value.number / 1000; // Convert MS to S
113    }
114
115    cleanup() {
116      // Clear all inline styles, otherwise they will throw off animations for images.
117      gsap.set(this.element, { clearProps: 'all' });
118    }
119
120    reset(element) {
121      const el = element || this.element;
122      el.classList.add(this.beforeClass);
123      el.classList.remove(this.completedClass);
124      el.classList.remove(this.animatingClass);
125    }
126
127    getOffset(offset, anchor) {
128      const defaultOffset = 120;
129
130      // Top-bottom placement looks terrible if the element is animated with offset zero.
131      if (anchor === 'top bottom' && offset.number === 0) {
132        return defaultOffset;
133      }
134
135      return offset.number;
136    }
137
138    createTween() {
139      const type = this.options.animation_type;
140      const animations = this.getAnimations();
141
142      if (!animations[type]) {
143        console.error(`[ENTRANCE] The selected ${type} animation is invalid.`);
144        return;
145      }
146
147      const [from, to] = animations[type];
148
149      const { ease, once, anchorPlacement } = this.options.advanced;
150      const duration = this.getDuration(this.options.duration);
151      const delay = this.getDuration(this.options.delay) + this.delay;
152      const offset = this.getOffset(this.options.advanced.offset, anchorPlacement);
153      const [elementStart, viewportStart] = anchorPlacement.split(' ');
154
155      const anim = gsap.timeline({
156        delay,
157        paused: true
158      });
159
160      this.element.classList.add(this.beforeClass);
161
162      if (isBuilder()) {
163        this.hideEl();
164      }
165
166      this.startTrigger = ScrollTrigger.create({
167        trigger: this.element,
168        start: `${elementStart}+=${offset} ${viewportStart}`,
169        toggleActions: "play none none none",
170        once,
171        onEnter: () => anim.play()
172      });
173
174      anim.fromTo(this.element, {
175        ...from,
176        autoAlpha: 0,
177      },
178      {
179        ...to,
180        duration,
181        delay,
182        ease,
183        autoAlpha: 1,
184        clearProps: 'all',
185        immediateRender: false,
186        onStart: () => {
187          this.element.classList.add(this.animatingClass);
188          this.element.classList.remove(this.beforeClass);
189        },
190        onComplete: () => {
191          this.element.classList.remove(this.animatingClass);
192          this.element.classList.add(this.completedClass);
193        },
194        onReverseComplete: () => {
195          this.element.classList.add(this.beforeClass);
196          this.element.classList.remove(this.animatingClass);
197        },
198      });
199
200      if (!once) {
201        this.goToBeginningOnReverse(anim);
202      }
203
204      return anim;
205    }
206
207    goToBeginningOnReverse(animation) {
208      // Reset animation to the beginning once its goes offscreen at the bottom.
209      this.endTrigger = ScrollTrigger.create({
210        trigger: this.element,
211        start: `top-=10 bottom`,
212        onLeaveBack: () => {
213          // Reset the animation to the beginning.
214          animation.pause(0);
215
216          // Clear all inline styles.
217          this.cleanup();
218
219          // Trick the builder into making the element selectable.
220          // This is needed because activating the element sets its visibility to hidden.
221          if (isBuilder()) {
222            this.hideEl();
223          }
224
225          this.reset();
226        }
227      });
228    }
229
230    hideEl() {
231      gsap.set(this.element, { autoAlpha: 0 });
232    }
233
234    update(options) {
235      this.options = mergeObjects(this.defaultOptions, options);
236      this.destroy();
237      this.init();
238    }
239
240    replay(delay = 0) {
241      this.destroy();
242      this.reset();
243      this.delay = delay;
244      this.init();
245    }
246
247    destroy() {
248      this.initialized = false;
249
250      if (!this.element) return;
251
252      this.element.classList.add(this.completedClass);
253
254      if (!this.tween) return;
255
256      this.tween.kill();
257      this.tween = null;
258      this.startTrigger?.kill();
259      this.endTrigger?.kill();
260
261      // Remove all inline styles
262      gsap.set(this.element, { clearProps: 'all' });
263    }
264
265    refresh() {
266      ScrollTrigger.refresh();
267    }
268
269    initTween() {
270      if (this.initialized) return;
271      this.initialized = true;
272
273      this.element.classList.remove(this.completedClass);
274      this.tween = this.createTween();
275    }
276
277    initOrDestroy() {
278      if (this.canAnimate()) {
279        this.initTween();
280      } else {
281        this.destroy();
282      }
283    }
284
285    init() {
286      if (!this.options.animation_type) return;
287
288      this.element = document.querySelector(this.selector);
289      this.element.bdAnim = this;
290
291      if (prefersReducedMotion()) {
292        this.element.classList.add(this.completedClass);
293        return;
294      }
295
296      onResize(() => this.initOrDestroy());
297      this.rootEl.classList.add(this.enabledClass);
298    }
299
300    static autoload() {
301      const loaded = imagesLoaded(document.body);
302
303      // Listen for images to be loaded.
304      loaded.on("always", () => {
305        // Refresh all instances.
306        const event = new Event("breakdance_refresh_animations", { bubbles: true });
307        document.dispatchEvent(event);
308
309        // Refresh ScrollTrigger only once.
310        ScrollTrigger.refresh(true);
311      });
312
313      // Play event
314      addEventListener("breakdance_play_animations", (event) => {
315        const target = event.target === window ? document.body : event.target;
316
317        const nodes = [
318          target,
319          ...target.querySelectorAll("[data-entrance]")
320        ];
321
322        nodes.forEach((el) => el.bdAnim?.replay(event?.detail?.delay));
323      });
324
325      // Cleanup event
326      addEventListener("breakdance_refresh_animations", (event) => {
327        const target = event.target === window ? document.body : event.target;
328
329        const nodes = [
330          target,
331          ...target.querySelectorAll("[data-entrance]")
332        ];
333
334        nodes.forEach((el) => el.bdAnim?.cleanup());
335      });
336
337      addEventListener("breakdance_reset_animations", (event) => {
338        const target = event.target === window ? document.body : event.target;
339
340        const nodes = [
341          target,
342          ...target.querySelectorAll("[data-entrance]")
343        ];
344
345        nodes.forEach((el) => el.bdAnim?.reset(el));
346      });
347    }
348
349    static dontLetScrollTriggerMutateScrollPosition() {
350      window.addEventListener("load", () => {
351        if (!location.hash) return;
352        if (window.bdeAnimationScrolled) return;
353        const scrollElem = document.querySelector(location.hash);
354        if (!scrollElem) return;
355
356        // Safari fix: Scroll to the element after the next repaint.
357        requestAnimationFrame(() => {
358          scrollElem.scrollIntoView({
359            behavior: "smooth",
360          });
361        });
362
363        // Prevent scrolling to the same element twice.
364        window.bdeAnimationScrolled = true;
365      });
366    }
367  }
368
369  window.BreakdanceEntrance = BreakdanceEntrance;
370
371  BreakdanceEntrance.autoload();
372  BreakdanceEntrance.dontLetScrollTriggerMutateScrollPosition();
373}());

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.