diff --git a/src/app/README-ROUTE-ANIMATIONS.md b/src/app/README-ROUTE-ANIMATIONS.md new file mode 100644 index 0000000..4b37e6a --- /dev/null +++ b/src/app/README-ROUTE-ANIMATIONS.md @@ -0,0 +1,108 @@ +# Animaciones de Ruta en PrimeNG Cronogramas + +Esta documentación explica cómo se implementaron las animaciones durante la navegación entre rutas en la aplicación. + +## Componentes Principales + +La solución consta de tres partes principales: + +1. **RouteAnimationsComponent**: Un componente contenedor para el router-outlet que aplica animaciones. +2. **CustomRouteReuseStrategy**: Una estrategia personalizada que fuerza la recreación de componentes. +3. **Configuración en app.config.ts**: Donde registramos la estrategia personalizada. + +## Cómo Funciona + +### 1. Componente de Animaciones de Ruta + +El `RouteAnimationsComponent` envuelve el router-outlet estándar y aplica una clase de animación aleatoria cada vez que ocurre una navegación. + +```typescript +// src/app/components/route-animations/route-animations.component.ts +``` + +Este componente: +- Se suscribe a los eventos de navegación del router +- Elimina brevemente la clase de animación +- Aplica una nueva animación aleatoria +- Todo esto crea un efecto de "reinicio" de la animación en cada navegación + +### 2. Estrategia de Reuso de Rutas + +Angular por defecto reutiliza componentes cuando navega entre rutas similares para optimizar el rendimiento. Sin embargo, para nuestras animaciones, necesitamos que los componentes se recreen. + +```typescript +// src/app/utils/custom-route-reuse-strategy.ts +``` + +Esta clase implementa `RouteReuseStrategy` y: +- Evita almacenar o recuperar componentes desactivados +- Solo reutiliza componentes cuando son exactamente la misma ruta +- Fuerza la recreación del componente en cada navegación + +### 3. Configuración en app.config.ts + +Registramos nuestra estrategia personalizada en el archivo de configuración de la aplicación: + +```typescript +// src/app/app.config.ts +{ + provide: RouteReuseStrategy, + useClass: CustomRouteReuseStrategy +} +``` + +### 4. Integración con el Layout + +En el layout principal, reemplazamos el router-outlet estándar con nuestro componente personalizado: + +```html + +
+ +
+``` + +## Animaciones Disponibles + +Las animaciones que se aplican aleatoriamente incluyen: +- fadeIn +- fadeInDown +- fadeInUp +- slideInLeft +- slideInRight +- zoomIn + +## Personalización + +Para modificar las animaciones disponibles, edita el array `animations` en el `RouteAnimationsComponent`: + +```typescript +private animations: string[] = [ + 'animate__fadeIn', + 'animate__fadeInDown', + // Añade o elimina animaciones aquí +]; +``` + +Todas las animaciones disponibles vienen de [Animate.css](https://animate.style/), asegúrate de que el nombre que elijas comience con `animate__` y exista en la biblioteca. + +## Ajustes de Rendimiento + +Si notas problemas de rendimiento: + +1. Puedes reducir la duración de las animaciones modificando: + ```css + .animate__animated { + animation-duration: 0.6s; /* Ajusta a un valor menor */ + } + ``` + +2. Limita las animaciones a solo las más simples (como fade) en lugar de las más complejas (como bounce o flip). + +## Depuración + +Si las animaciones no funcionan: + +1. Verifica que `animate.css` esté correctamente importado en tu `angular.json` +2. Asegúrate de que el componente `RouteAnimationsComponent` esté correctamente importado en el layout +3. Comprueba que la estrategia de reuso de rutas esté registrada en `app.config.ts` \ No newline at end of file diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 8b3ddeb..975748c 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -1,5 +1,5 @@ import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; -import { provideRouter } from '@angular/router'; +import { provideRouter, RouteReuseStrategy } from '@angular/router'; import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http'; import { routes } from './app.routes'; @@ -7,6 +7,7 @@ import { provideAnimations } from '@angular/platform-browser/animations'; import { providePrimeNG } from 'primeng/config'; import Aura from '@primeng/themes/aura'; import { authInterceptor } from './interceptors/auth.interceptor'; +import { CustomRouteReuseStrategy } from './utils/custom-route-reuse-strategy'; export const appConfig: ApplicationConfig = { providers: [ @@ -21,6 +22,12 @@ export const appConfig: ApplicationConfig = { darkModeSelector: false || 'none' } } - }) + }), + // Añadir estrategia de reuso de rutas personalizada para forzar + // recreación de componentes en cada navegación + { + provide: RouteReuseStrategy, + useClass: CustomRouteReuseStrategy + } ] }; diff --git a/src/app/components/layout/layout.component.html b/src/app/components/layout/layout.component.html index 7c958fc..6031043 100644 --- a/src/app/components/layout/layout.component.html +++ b/src/app/components/layout/layout.component.html @@ -12,9 +12,9 @@ - +
- +
diff --git a/src/app/components/layout/layout.component.ts b/src/app/components/layout/layout.component.ts index 9f4d1fa..66b89c0 100644 --- a/src/app/components/layout/layout.component.ts +++ b/src/app/components/layout/layout.component.ts @@ -4,6 +4,7 @@ import { RouterModule } from '@angular/router'; import { NavbarComponent } from '../navbar/navbar.component'; import { SidebarComponent } from '../sidebar/sidebar.component'; import { FooterComponent } from "../footer/footer.component"; +import { RouteAnimationsComponent } from '../route-animations/route-animations.component'; @Component({ selector: 'app-layout', @@ -12,8 +13,9 @@ import { FooterComponent } from "../footer/footer.component"; RouterModule, NavbarComponent, SidebarComponent, - FooterComponent -], + FooterComponent, + RouteAnimationsComponent + ], templateUrl: './layout.component.html', styleUrl: './layout.component.scss', standalone: true, diff --git a/src/app/components/route-animations/route-animations.component.ts b/src/app/components/route-animations/route-animations.component.ts new file mode 100644 index 0000000..f11b653 --- /dev/null +++ b/src/app/components/route-animations/route-animations.component.ts @@ -0,0 +1,59 @@ +import { Component, OnDestroy } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Router, RouterOutlet, NavigationEnd, Event } from '@angular/router'; +import { Subscription, filter } from 'rxjs'; + +@Component({ + selector: 'app-route-animations', + standalone: true, + imports: [CommonModule, RouterOutlet], + template: ` +
+ +
+ `, + styles: [` + .content-container { + width: 100%; + height: 100%; + } + + .animate__animated { + animation-duration: 0.5s; + } + `] +}) +export class RouteAnimationsComponent implements OnDestroy { + // Clase de animación actual + animationClass: string = 'animate__animated animate__fadeIn '; + + // Animaciones disponibles + private animations: string[] = [ + 'animate__fadeIn' + ]; + + private routerSub: Subscription; + + constructor(private router: Router) { + // Suscribirse a los eventos de navegación + this.routerSub = this.router.events + .pipe(filter((event: Event) => event instanceof NavigationEnd)) + .subscribe(() => { + // Resetear la animación primero + this.animationClass = ''; + + // Aplicar una nueva animación después de un breve retraso + setTimeout(() => { + const randomIndex = Math.floor(Math.random() * this.animations.length); + this.animationClass = `animate__animated ${this.animations[randomIndex]} `; + }, 50); + }); + } + + ngOnDestroy(): void { + // Cancelar suscripción para evitar memory leaks + if (this.routerSub) { + this.routerSub.unsubscribe(); + } + } +} \ No newline at end of file diff --git a/src/app/utils/custom-route-reuse-strategy.ts b/src/app/utils/custom-route-reuse-strategy.ts new file mode 100644 index 0000000..9f585d3 --- /dev/null +++ b/src/app/utils/custom-route-reuse-strategy.ts @@ -0,0 +1,42 @@ +import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router'; + +/** + * Estrategia personalizada que evita la reutilización de rutas + * para asegurar que los componentes se vuelvan a crear en cada navegación + */ +export class CustomRouteReuseStrategy implements RouteReuseStrategy { + /** + * No almacenar ninguna ruta al desactivarse + */ + shouldDetach(route: ActivatedRouteSnapshot): boolean { + return false; + } + + /** + * No hay rutas almacenadas, así que esto nunca se llama + */ + store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle | null): void {} + + /** + * No recuperar rutas almacenadas + */ + shouldAttach(route: ActivatedRouteSnapshot): boolean { + return false; + } + + /** + * No hay rutas almacenadas, así que esto nunca se llama + */ + retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null { + return null; + } + + /** + * No reutilizar rutas para forzar la recreación de componentes + */ + shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { + // Solo reutilizar si es la misma ruta exactamente (evita problemas con rutas anidadas) + return future.routeConfig === curr.routeConfig && + JSON.stringify(future.params) === JSON.stringify(curr.params); + } +} \ No newline at end of file