diff --git a/src/app/components/animate/animate.ts b/src/app/components/animate/animate.ts new file mode 100644 index 00000000000..04b42426671 --- /dev/null +++ b/src/app/components/animate/animate.ts @@ -0,0 +1,144 @@ +import { NgModule, Directive, ElementRef, Input, Renderer2, Inject, NgZone } from '@angular/core'; +import { CommonModule, DOCUMENT } from '@angular/common'; +import { DomHandler } from '../dom/domhandler'; + +@Directive({ + selector: '[pAnimate]', + host: { + '[class.p-animate]': 'true' + } +}) +export class Animate { + + @Input() enterClass: string; + + @Input() leaveClass: string; + + documentScrollListener: Function | null = null; + + loadListener: Function = () => { }; + + entered: boolean; + + observer: IntersectionObserver; + + loaded: boolean; + + constructor(@Inject(DOCUMENT) private document: Document, private host: ElementRef, public el: ElementRef, public renderer: Renderer2, private zone: NgZone) { } + + ngOnInit() { + if (this.isInViewport()) { + this.enter(); + } + this.bindLoadListener(); + } + + bindIntersectionObserver() { + const options = { + root: null, + rootMargin: '0px', + threshold: 1.0 + } + + this.zone.runOutsideAngular(() => { + this.observer = new IntersectionObserver(el => this.isVisible(el), options); + this.observer.observe(this.host.nativeElement); + }) + } + + isInViewport() { + let rect = this.host.nativeElement.getBoundingClientRect(); + + return ( + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= ((window.innerHeight + rect.height) || this.document.documentElement.clientHeight) && + rect.right <= (window.innerWidth || this.document.documentElement.clientWidth) + ); + } + + isVisible(element: IntersectionObserverEntry[]) { + const [intersectionObserverEntry] = element; + this.entered = intersectionObserverEntry.isIntersecting; + } + + animate() { + if (this.loaded) { + if (this.isInViewport() && this.entered) { + this.enter(); + } + + if (!this.isInViewport() && !this.entered) { + this.leave(); + } + } + } + + enter() { + this.host.nativeElement.style.visibility = 'visible'; + DomHandler.addClass(this.host.nativeElement, this.enterClass); + } + + leave() { + DomHandler.removeClass(this.host.nativeElement, this.enterClass); + this.host.nativeElement.style.visibility = 'hidden'; + } + + bindDocumentScrollListener() { + if (!this.documentScrollListener) { + this.zone.runOutsideAngular(() => { + this.documentScrollListener = this.renderer.listen(window, 'scroll', () => { + if (!this.observer) { + this.bindIntersectionObserver(); + } + this.animate(); + }) + }) + } + } + + unbindDocumentScrollListener() { + if (this.documentScrollListener) { + this.documentScrollListener(); + this.documentScrollListener = null; + } + } + + bindLoadListener() { + this.zone.runOutsideAngular(() => { + this.loadListener = this.renderer.listen(window, 'load', () => { + if (!this.loaded) { + this.animate(); + } + if (!this.documentScrollListener) { + this.bindDocumentScrollListener(); + } + this.loaded = true; + }); + }) + } + + unbindLoadListener() { + if (this.loadListener) { + this.loadListener(); + this.loadListener = null; + } + } + + unbindIntersectionObserver() { + this.observer.unobserve(this.host.nativeElement); + } + + ngOnDestroy() { + this.unbindDocumentScrollListener(); + this.unbindLoadListener(); + this.unbindIntersectionObserver(); + } +} + +@NgModule({ + imports: [CommonModule], + exports: [Animate], + declarations: [Animate] +}) +export class AnimateModule { } diff --git a/src/app/components/animate/ng-package.json b/src/app/components/animate/ng-package.json new file mode 100644 index 00000000000..0e529e387d7 --- /dev/null +++ b/src/app/components/animate/ng-package.json @@ -0,0 +1,6 @@ +{ + "$schema": "ng-packagr/ng-package.schema.json", + "lib": { + "entryFile": "public_api.ts" + } +} \ No newline at end of file diff --git a/src/app/components/animate/public_api.ts b/src/app/components/animate/public_api.ts new file mode 100644 index 00000000000..a4e45474233 --- /dev/null +++ b/src/app/components/animate/public_api.ts @@ -0,0 +1 @@ +export * from './animate'; \ No newline at end of file diff --git a/src/app/showcase/app-routing.module.ts b/src/app/showcase/app-routing.module.ts index c1bf88a7a56..94bda46e45f 100755 --- a/src/app/showcase/app-routing.module.ts +++ b/src/app/showcase/app-routing.module.ts @@ -121,7 +121,8 @@ import { LandingComponent } from './components/landing/landing.component'; { path: 'scroller', loadChildren: () => import('./components/scroller/scrollerdemo.module').then((m) => m.ScrollerDemoModule) }, { path: 'uikit', loadChildren: () => import('./components/uikit/uikit.module').then((m) => m.UIKitModule) }, { path: 'autofocus', loadChildren: () => import('./components/autofocus/autofocusdemo.module').then((m) => m.AutoFocusDemoModule) }, - { path: 'overlay', loadChildren: () => import('./components/overlay/overlaydemo.module').then((m) => m.OverlayDemoModule) } + { path: 'overlay', loadChildren: () => import('./components/overlay/overlaydemo.module').then((m) => m.OverlayDemoModule) }, + { path: 'animate', loadChildren: () => import('./components/animate/animatedemo.module').then((m) => m.AnimateDemoModule) } ] } ], diff --git a/src/app/showcase/app.menu.component.ts b/src/app/showcase/app.menu.component.ts index 6deb9830d58..61fe64e7c88 100644 --- a/src/app/showcase/app.menu.component.ts +++ b/src/app/showcase/app.menu.component.ts @@ -338,6 +338,7 @@ declare let gtag: Function; StyleClass Ripple AutoFocusNew + AnimateNew diff --git a/src/app/showcase/components/animate/animate-routing.module.ts b/src/app/showcase/components/animate/animate-routing.module.ts new file mode 100755 index 00000000000..bf1a36077ec --- /dev/null +++ b/src/app/showcase/components/animate/animate-routing.module.ts @@ -0,0 +1,15 @@ +import {NgModule} from '@angular/core'; +import {RouterModule} from '@angular/router' +import {AnimateDemo} from './animatedemo'; + +@NgModule({ + imports: [ + RouterModule.forChild([ + {path:'',component: AnimateDemo} + ]) + ], + exports: [ + RouterModule + ] +}) +export class AnimateDemoRoutingModule {} diff --git a/src/app/showcase/components/animate/animatedemo.html b/src/app/showcase/components/animate/animatedemo.html new file mode 100755 index 00000000000..bba20085fef --- /dev/null +++ b/src/app/showcase/components/animate/animatedemo.html @@ -0,0 +1,76 @@ +
+
+

Animate

+

Animate manages PrimeFlex CSS classes declaratively to during enter animations on scroll or on page load.

+
+ +
+ +
+
+
+ flip +
+
+
+ flipup +
+
+
+ flipleft +
+
+
+ +
+ + +
Import
+ +import {AnimateModule} from 'primeng/animate'; + + +
Getting Started
+

Animate uses PrimeFlex animations, however it can perform animations with custom CSS classes too. Takes enterClass property to simply add animation class during scroll or page load to manage elements animation if the element is in viewport.

+ +

Enter Animation

+ +<div pAnimate enterClass="flip" class="flex justify-content-center align-items-center h-20rem w-20rem border-round shadow-2 animation-duration-1000 animation-ease-out"></div> + + +
Properties
+
+ + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
enterClassstringnullSelector to define the CSS class for animation.
+
+ +
Events
+

Directive has no events.

+ +
Dependencies
+

None.

+
+ + + + View on GitHub + + +
+
diff --git a/src/app/showcase/components/animate/animatedemo.module.ts b/src/app/showcase/components/animate/animatedemo.module.ts new file mode 100755 index 00000000000..26260a4ffc4 --- /dev/null +++ b/src/app/showcase/components/animate/animatedemo.module.ts @@ -0,0 +1,25 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {FormsModule} from '@angular/forms' +import {AnimateDemo} from './animatedemo'; +import {AnimateDemoRoutingModule} from './animate-routing.module'; +import {AnimateModule} from 'primeng/animate'; +import {TabViewModule} from 'primeng/tabview'; +import {AppCodeModule} from '../../app.code.component'; +import {AppDemoActionsModule} from '../../app.demoactions.component'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + AnimateDemoRoutingModule, + AnimateModule, + TabViewModule, + AppDemoActionsModule, + AppCodeModule + ], + declarations: [ + AnimateDemo + ] +}) +export class AnimateDemoModule {} diff --git a/src/app/showcase/components/animate/animatedemo.ts b/src/app/showcase/components/animate/animatedemo.ts new file mode 100755 index 00000000000..cbd0c0e6768 --- /dev/null +++ b/src/app/showcase/components/animate/animatedemo.ts @@ -0,0 +1,6 @@ +import { Component } from '@angular/core'; + +@Component({ + templateUrl: './animatedemo.html' +}) +export class AnimateDemo { } \ No newline at end of file