Skip to content

Commit

Permalink
Merge pull request #12067 from primefaces/issue-12057
Browse files Browse the repository at this point in the history
Animate directive
  • Loading branch information
cetincakiroglu authored Nov 8, 2022
2 parents e05b79d + b7f1b37 commit 954104d
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 1 deletion.
144 changes: 144 additions & 0 deletions src/app/components/animate/animate.ts
Original file line number Diff line number Diff line change
@@ -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 { }
6 changes: 6 additions & 0 deletions src/app/components/animate/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "ng-packagr/ng-package.schema.json",
"lib": {
"entryFile": "public_api.ts"
}
}
1 change: 1 addition & 0 deletions src/app/components/animate/public_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './animate';
3 changes: 2 additions & 1 deletion src/app/showcase/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
]
}
],
Expand Down
1 change: 1 addition & 0 deletions src/app/showcase/app.menu.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ declare let gtag: Function;
<a [routerLink]="['styleclass']" routerLinkActive="router-link-exact-active">StyleClass</a>
<a [routerLink]="['ripple']" routerLinkActive="router-link-exact-active">Ripple</a>
<a [routerLink]="['autofocus']" routerLinkActive="router-link-exact-active">AutoFocus<span class="p-tag">New</span></a>
<a [routerLink]="['animate']" routerLinkActive="router-link-exact-active">Animate<span class="p-tag">New</span></a>
</div>
<div class="menu-category">Utilities</div>
Expand Down
15 changes: 15 additions & 0 deletions src/app/showcase/components/animate/animate-routing.module.ts
Original file line number Diff line number Diff line change
@@ -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 {}
76 changes: 76 additions & 0 deletions src/app/showcase/components/animate/animatedemo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<div class="content-section introduction">
<div class="feature-intro">
<h1>Animate</h1>
<p>Animate manages PrimeFlex CSS classes declaratively to during enter animations on scroll or on page load.</p>
</div>
<app-demoActions github="animate" stackblitz="animate-demo"></app-demoActions>
</div>

<div class="content-section implementation">
<div class="card flex flex-column align-items-center">
<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">
<span class="text-900 text-3xl font-bold">flip</span>
</div>
<div class="h-30rem"></div>
<div pAnimate enterClass="flipup" class="flex justify-content-center align-items-center h-20rem w-20rem border-round shadow-2 animation-duration-1000 animation-ease-out">
<span class="text-900 text-3xl font-bold">flipup</span>
</div>
<div class="h-30rem"></div>
<div pAnimate enterClass="flipleft" class="flex justify-content-center align-items-center h-20rem w-20rem border-round shadow-2 animation-duration-1000 animation-ease-out">
<span class="text-900 text-3xl font-bold">flipleft</span>
</div>
</div>
</div>

<div class="content-section documentation">
<p-tabView>
<p-tabPanel header="Documentation">
<h5>Import</h5>
<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
import &#123;AnimateModule&#125; from 'primeng/animate';
</app-code>

<h5>Getting Started</h5>
<p>Animate uses <a href="http://primefaces.org/primeflex/animations">PrimeFlex animations</a>, however it can perform animations with custom CSS classes too. Takes <i>enterClass</i> property to simply add animation class during scroll or page load to manage elements animation if the element is in viewport.</p>

<p><b>Enter Animation</b></p>
<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;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"&gt;&lt;/div&gt;
</app-code>

<h5>Properties</h5>
<div class="doc-tablewrapper">
<table class="doc-table">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>enterClass</td>
<td>string</td>
<td>null</td>
<td>Selector to define the CSS class for animation.</td>
</tr>
</tbody>
</table>
</div>

<h5>Events</h5>
<p>Directive has no events.</p>

<h5>Dependencies</h5>
<p>None.</p>
</p-tabPanel>

<p-tabPanel header="Source">
<a href="https://github.com/primefaces/primeng/tree/master/src/app/showcase/components/animate" class="btn-viewsource" target="_blank">
<span>View on GitHub</span>
</a>
</p-tabPanel>
</p-tabView>
</div>
25 changes: 25 additions & 0 deletions src/app/showcase/components/animate/animatedemo.module.ts
Original file line number Diff line number Diff line change
@@ -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 {}
6 changes: 6 additions & 0 deletions src/app/showcase/components/animate/animatedemo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Component } from '@angular/core';

@Component({
templateUrl: './animatedemo.html'
})
export class AnimateDemo { }

0 comments on commit 954104d

Please sign in to comment.