PageSourceSearch

https://sportingclubhuelva.com/js/wf_scrollanim.js?v=1779270955

js sportingclubhuelva.com collected 2026-10-02 13:36:40 UTC 6,389 bytes, 293 lines download raw bytes

1/*!
2 * wf_scrollanim.js
3 * Reemplazo moderno de kissui.scrollanim
4 * Compatible con:
5 * data-kui-anim="fadeInUp"
6 * data-kui-event="in"
7 */
8
9class wf_scrollanim
10{
11	constructor(opciones = {})
12	{
13		this.opciones = {
14			selector         : `[data-kui-anim]`,
15			prefijoAnimado   : `animated`,
16			atributoAnim     : `data-kui-anim`,
17			atributoEvento   : `data-kui-event`,
18			atributoDelay    : `data-kui-delay`,
19			atributoDuration : `data-kui-duration`,
20			autoReset        : true,
21			triggerOnInit    : true,
22			debug            : false,
23			...opciones
24		};
25
26		this.items = [];
27		this.observer = null;
28		this._init();
29	}
30
31	log(...args)
32	{
33		if (this.opciones.debug)
34			console.log(`[wf_scrollanim]`, ...args);
35	}
36
37	_init()
38	{
39		const elementos = document.querySelectorAll(this.opciones.selector);
40
41		if (!elementos.length)
42		{
43			this.log(`No se encontraron elementos.`);
44			return;
45		}
46
47		this.items = Array.from(elementos).map((elemento) =>
48		{
49			const animacion = (elemento.getAttribute(this.opciones.atributoAnim) || ``).trim();
50			const evento = (elemento.getAttribute(this.opciones.atributoEvento) || `in`).trim();
51			const delay = (elemento.getAttribute(this.opciones.atributoDelay) || ``).trim();
52			const duration = (elemento.getAttribute(this.opciones.atributoDuration) || ``).trim();
53
54			if (!animacion)
55				return null;
56
57			elemento.style.opacity = `0`;
58
59			if (delay !== ``)
60				elemento.style.animationDelay = delay;
61
62			if (duration !== ``)
63				elemento.style.animationDuration = duration;
64
65			return {
66				elemento,
67				animacion,
68				evento,
69				activa   : false, // animación en curso
70				animada  : false, // ya se animó en esta entrada al viewport
71				visible  : false  // está actualmente visible
72			};
73		}).filter(Boolean);
74
75		this._crearObserver();
76
77		this.items.forEach((item) =>
78		{
79			this.observer.observe(item.elemento);
80		});
81
82		if (this.opciones.triggerOnInit)
83			this._comprobarManual();
84	}
85
86	_crearObserver()
87	{
88		this.observer = new IntersectionObserver((entradas) =>
89		{
90			entradas.forEach((entrada) =>
91			{
92				const item = this.items.find((it) => it.elemento === entrada.target);
93				if (!item) return;
94
95				const rect = entrada.boundingClientRect;
96				const viewportH = window.innerHeight || document.documentElement.clientHeight;
97				const viewportW = window.innerWidth || document.documentElement.clientWidth;
98
99				const visibleReal =
100					rect.bottom > 0 &&
101					rect.right > 0 &&
102					rect.top < viewportH &&
103					rect.left < viewportW &&
104					entrada.isIntersecting;
105
106				item.visible = visibleReal;
107
108				// Mientras siga visible, jamás se reanima.
109				if (visibleReal)
110				{
111					if (!item.animada)
112					{
113						this._animar(item);
114						item.animada = true;
115					}
116				}
117				else
118				{
119					// Sólo al salir completamente de pantalla se rearma.
120					this._reset(item);
121				}
122			});
123		}, {
124			threshold : 0
125		});
126	}
127
128	_comprobarManual()
129	{
130		this.items.forEach((item) =>
131		{
132			const rect = item.elemento.getBoundingClientRect();
133			const viewportH = window.innerHeight || document.documentElement.clientHeight;
134			const viewportW = window.innerWidth || document.documentElement.clientWidth;
135
136			const visible =
137				rect.bottom > 0 &&
138				rect.right > 0 &&
139				rect.top < viewportH &&
140				rect.left < viewportW;
141
142			item.visible = visible;
143
144			if (visible && !item.animada)
145			{
146				this._animar(item);
147				item.animada = true;
148			}
149		});
150	}
151
152	_cumpleEvento(evento, estado)
153	{
154		const eventos = evento.split(/\s+/).filter(Boolean);
155
156		return eventos.every((ev) =>
157		{
158			switch (ev)
159			{
160				case `in`:
161					return estado.visible;
162
163				case `out`:
164					return !estado.visible;
165
166				case `middle`:
167					return this._puntoEnRango(
168						estado.viewportH / 2,
169						estado.rect.top,
170						estado.rect.bottom
171					);
172
173				case `center`:
174					return this._puntoEnRango(
175						estado.viewportW / 2,
176						estado.rect.left,
177						estado.rect.right
178					);
179
180				case `top`:
181					return this._puntoEnRango(0, estado.rect.top, estado.rect.bottom);
182
183				case `bottom`:
184					return this._puntoEnRango(
185						estado.viewportH,
186						estado.rect.top,
187						estado.rect.bottom
188					);
189
190				case `left`:
191					return this._puntoEnRango(0, estado.rect.left, estado.rect.right);
192
193				case `right`:
194					return this._puntoEnRango(
195						estado.viewportW,
196						estado.rect.left,
197						estado.rect.right
198					);
199
200				default:
201					this.log(`Evento no soportado:`, ev);
202					return false;
203			}
204		});
205	}
206
207	_puntoEnRango(punto, inicio, fin)
208	{
209		return punto >= inicio && punto <= fin;
210	}
211
212	_clasesAnimacion(animacion)
213	{
214		return String(animacion || ``)
215			.trim()
216			.split(/\s+/)
217			.filter(Boolean)
218			.map((clase) =>
219			{
220				if (clase.startsWith(`animate__`))
221					return clase;
222
223				return `animate__${clase}`;
224			});
225	}
226
227	_animar(item)
228	{
229		if (item.activa)
230			return;
231
232		const el = item.elemento;
233		const claseBase = `animate__animated`;
234		const clasesAnim = this._clasesAnimacion(item.animacion);
235
236		el.style.opacity = `1`;
237		el.classList.remove(claseBase, ...clasesAnim);
238
239		void el.offsetWidth;
240
241		el.classList.add(claseBase, ...clasesAnim);
242		item.activa = true;
243
244		const finAnim = () =>
245		{
246			el.classList.remove(claseBase, ...clasesAnim);
247			el.removeEventListener(`animationend`, finAnim);
248
249			item.activa = false;
250		};
251
252		el.addEventListener(`animationend`, finAnim);
253	}
254
255	_reset(item)
256	{
257		const clasesAnim = this._clasesAnimacion(item.animacion);
258
259		item.activa = false;
260		item.animada = false;
261		item.visible = false;
262		item.elemento.style.opacity = `0`;
263		item.elemento.classList.remove(`animate__animated`, ...clasesAnim);
264	}
265
266	refresh()
267	{
268		if (this.observer)
269			this.observer.disconnect();
270
271		this.items = [];
272		this._init();
273	}
274
275	destroy()
276	{
277		if (this.observer)
278			this.observer.disconnect();
279
280		this.items.forEach((item) =>
281		{
282			const clasesAnim = this._clasesAnimacion(item.animacion);
283
284			item.elemento.classList.remove(`animate__animated`, ...clasesAnim);
285			item.elemento.style.opacity = ``;
286			item.elemento.style.animationDelay = ``;
287			item.elemento.style.animationDuration = ``;
288		});
289
290		this.items = [];
291		this.observer = null;
292	}
293}

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.