UNPKG

ngx-flip-card

Version:

Angular component to animate a double-sided card.

313 lines (307 loc) 16 kB
import * as i0 from '@angular/core'; import { EventEmitter, Component, Input, Output, HostListener, ViewChild, NgModule } from '@angular/core'; import * as i1 from '@angular/common'; import { CommonModule } from '@angular/common'; class NgxFlipCardComponent { constructor() { this._isBackSide = false; this.isBackSideChange = new EventEmitter(); this.animate = true; this.animationDuration = 2500; this.animationEnd = new EventEmitter(); this.canAnimate = false; this.frontRotation = 0; this.backRotation = 180; this.desiredFrontRotation = 0; this.frontZindex = 1; this.backZindex = 0; this.translationZ = 2; this.currentTranslationZ = 0; this.easing = 'linear'; this.direction = 'horizontal'; this.flipOnClick = true; this.timeLastFrame = Date.now(); this.currentAnimationTime = 0; this.animating = false; } set isBackSide(value) { if (value === this._isBackSide) { return; } this._isBackSide = value; this.isBackSideChange.emit(this._isBackSide); this.flipInternal(); } get isBackSide() { return this._isBackSide; } ngOnInit() { } ngAfterViewInit() { this.canAnimate = true; } flipInternal() { this.rotateAction(); } clickHandler() { if (this.flipOnClick) { this.flipCard(); } } flipCard() { this.isBackSide = !this._isBackSide; } rotateAction() { if (!this.animate || !this.canAnimate) { this.rotateFlat(); } else { this.animateRotationStart(); } } rotateFlat() { if (this.isBackSide) { this.frontRotation = 180; this.backRotation = 0; this.frontZindex = 0; this.backZindex = 1; } else { this.frontRotation = 0; this.backRotation = 180; this.frontZindex = 1; this.backZindex = 0; } } animateRotationStart() { this.desiredFrontRotation = this.isBackSide ? 180 : 0; this.timeLastFrame = Date.now(); if (this.animating) { this.currentAnimationTime = this.calculateReverseEasing(); } else { this.currentAnimationTime = 0; } if (this.animationFrameRef) { cancelAnimationFrame(this.animationFrameRef); } this.animationFrameRef = requestAnimationFrame(this.animateLoop.bind(this)); this.animating = true; this.currentTranslationZ = this.translationZ; } animateLoop() { const time = Date.now(); const deltatime = time - this.timeLastFrame; this.timeLastFrame = time; this.currentAnimationTime += deltatime; const amount = this.currentAnimationTime / this.animationDuration; switch (this.easing) { case 'linear': this.frontRotation = this.isBackSide ? Math.min(this.lerp(0, this.desiredFrontRotation, amount), this.desiredFrontRotation) : Math.max(this.lerp(180, this.desiredFrontRotation, amount), this.desiredFrontRotation); break; case 'easeIn': this.frontRotation = this.isBackSide ? Math.min(this.easeIn(0, this.desiredFrontRotation, amount), this.desiredFrontRotation) : Math.max(this.easeIn(180, this.desiredFrontRotation, amount), this.desiredFrontRotation); break; case 'easeOut': this.frontRotation = this.isBackSide ? Math.min(this.easeOut(0, this.desiredFrontRotation, amount), this.desiredFrontRotation) : Math.max(this.easeOut(180, this.desiredFrontRotation, amount), this.desiredFrontRotation); break; case 'easeInOut': this.frontRotation = this.isBackSide ? Math.min(this.easeInOut(0, this.desiredFrontRotation, amount), this.desiredFrontRotation) : Math.max(this.easeInOut(180, this.desiredFrontRotation, amount), this.desiredFrontRotation); break; } if (this.currentAnimationTime >= this.animationDuration) { this.frontRotation = this.desiredFrontRotation; } this.backRotation = this.frontRotation + 180; if (this.isBackSide && this.frontRotation >= 90) { this.frontZindex = 0; this.backZindex = 1; } else if (!this.isBackSide && this.frontRotation <= 90) { this.frontZindex = 1; this.backZindex = 0; } if (this.frontRotation === this.desiredFrontRotation) { this.animationEnded(); return; } this.animationFrameRef = requestAnimationFrame(this.animateLoop.bind(this)); } lerp(a, b, n) { return (1 - n) * a + n * b; } easeIn(a, b, n) { return this.lerp(a, b, n * n); } easeOut(a, b, n) { return this.lerp(a, b, 1 - (1 - n) * (1 - n)); } easeInOut(a, b, n) { return this.lerp(a, b, n < 0.5 ? 2 * n * n : 1 - Math.pow(-2 * n + 2, 2) / 2); } calculateReverseEasing() { if (this.easing === 'linear') { return this.animationDuration - this.currentAnimationTime; } const start = this._isBackSide ? 0 : 180; const end = this._isBackSide ? 180 : 0; // increment 16 milliseconds to simulate animation at 60FPS for (let i = 0; i < this.animationDuration; i += 16) { let inverseValue = 0; switch (this.easing) { case 'easeIn': inverseValue = this.easeIn(start, end, i / this.animationDuration); break; case 'easeOut': inverseValue = this.easeOut(start, end, i / this.animationDuration); break; case 'easeInOut': inverseValue = this.easeInOut(start, end, i / this.animationDuration); break; } if ((this._isBackSide && inverseValue >= this.frontRotation) || (!this._isBackSide && inverseValue <= this.frontRotation)) { return i; } } return 0; } animationEnded() { this.animationEnd.emit(this.isBackSide); this.animating = false; this.currentTranslationZ = 0; } } NgxFlipCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: NgxFlipCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); NgxFlipCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: NgxFlipCardComponent, selector: "ngx-flip-card", inputs: { isBackSide: "isBackSide", animate: "animate", animationDuration: "animationDuration", translationZ: "translationZ", easing: "easing", direction: "direction", flipOnClick: "flipOnClick" }, outputs: { isBackSideChange: "isBackSideChange", animationEnd: "animationEnd" }, ngImport: i0, template: "<div class=\"card-container\" (click)=\"clickHandler()\">\n <div class=\"card-side\"\n [ngStyle]=\"{ 'transform': (direction === 'horizontal' ? 'rotateY(' + frontRotation + 'deg)' : 'rotateX(' + frontRotation + 'deg)') + ' translateZ(' + currentTranslationZ + 'px )', 'z-index': frontZindex }\">\n <ng-content select=\"[card-front]\"></ng-content>\n </div>\n <div class=\"card-side\" \n [ngStyle]=\"{ 'transform': (direction === 'horizontal' ? 'rotateY(' + backRotation + 'deg)' : 'rotateX(' + backRotation + 'deg)') + ' translateZ(' + currentTranslationZ + 'px )', 'z-index': backZindex }\">\n <ng-content select=\"[card-back]\"></ng-content>\n </div>\n</div>\n", styles: [".card-container{display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);position:relative;perspective:500px;width:-moz-fit-content;width:fit-content}.card-side{grid-row:1 / -1;grid-column:1 / -1;transform-origin:center}\n"], directives: [{ type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: NgxFlipCardComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-flip-card', template: "<div class=\"card-container\" (click)=\"clickHandler()\">\n <div class=\"card-side\"\n [ngStyle]=\"{ 'transform': (direction === 'horizontal' ? 'rotateY(' + frontRotation + 'deg)' : 'rotateX(' + frontRotation + 'deg)') + ' translateZ(' + currentTranslationZ + 'px )', 'z-index': frontZindex }\">\n <ng-content select=\"[card-front]\"></ng-content>\n </div>\n <div class=\"card-side\" \n [ngStyle]=\"{ 'transform': (direction === 'horizontal' ? 'rotateY(' + backRotation + 'deg)' : 'rotateX(' + backRotation + 'deg)') + ' translateZ(' + currentTranslationZ + 'px )', 'z-index': backZindex }\">\n <ng-content select=\"[card-back]\"></ng-content>\n </div>\n</div>\n", styles: [".card-container{display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);position:relative;perspective:500px;width:-moz-fit-content;width:fit-content}.card-side{grid-row:1 / -1;grid-column:1 / -1;transform-origin:center}\n"] }] }], ctorParameters: function () { return []; }, propDecorators: { isBackSide: [{ type: Input }], isBackSideChange: [{ type: Output }], animate: [{ type: Input }], animationDuration: [{ type: Input }], animationEnd: [{ type: Output }], translationZ: [{ type: Input }], easing: [{ type: Input }], direction: [{ type: Input }], flipOnClick: [{ type: Input }] } }); class NgxTiltCardComponent { constructor() { this.xRot = 0; this.yRot = 0; this.rotationDegreesX = 5; this.rotationDegreesY = 5; this.xRotationMatrix = []; this.yRotationMatrix = []; } onMouseOut() { this.xRot = 0; this.yRot = 0; } onMouseMove(event) { let index = event.offsetY / this.tiltContentRef.nativeElement.offsetHeight; index = Math.floor(index * 5); index = index > this.xRotationMatrix.length - 1 ? this.xRotationMatrix.length - 1 : index; if (index < this.xRotationMatrix.length && this.xRot !== this.xRotationMatrix[index]) { this.xRot = this.xRotationMatrix[index]; } else if (index >= this.xRotationMatrix.length) { this.xRot = 0; this.yRot = 0; return; } index = event.offsetX / this.tiltContentRef.nativeElement.offsetWidth; index = Math.floor(index * 5); if (index < this.yRotationMatrix.length && this.yRot !== this.yRotationMatrix[index]) { this.yRot = this.yRotationMatrix[index]; } else if (index >= this.yRotationMatrix.length) { this.xRot = 0; this.yRot = 0; } } ngOnInit() { this.xRotationMatrix = [ this.rotationDegreesX, this.rotationDegreesX / 2, 0, (this.rotationDegreesX * -1) / 2, this.rotationDegreesX * -1 ]; this.yRotationMatrix = [ this.rotationDegreesY * -1, (this.rotationDegreesY * -1) / 2, 0, this.rotationDegreesY / 2, this.rotationDegreesY ]; } } NgxTiltCardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: NgxTiltCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); NgxTiltCardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: NgxTiltCardComponent, selector: "ngx-tilt-card", inputs: { rotationDegreesX: "rotationDegreesX", rotationDegreesY: "rotationDegreesY" }, host: { listeners: { "mouseout": "onMouseOut()", "mousemove": "onMouseMove($event)" } }, viewQueries: [{ propertyName: "tiltContentRef", first: true, predicate: ["tilt"], descendants: true }], ngImport: i0, template: "<div class=\"tilt-container\">\n <div #tilt class=\"tilt-content\" [ngStyle]=\"{'transform': 'rotateX(' + xRot + 'deg) rotateY(' + yRot + 'deg)'}\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [".tilt-container{display:grid;grid-template-columns:1fr;grid-template-rows:1fr;position:relative;perspective:500px;width:-moz-fit-content;width:fit-content}.tilt-content{transform-origin:center;position:relative;transition:transform 1s ease}\n"], directives: [{ type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: NgxTiltCardComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-tilt-card', template: "<div class=\"tilt-container\">\n <div #tilt class=\"tilt-content\" [ngStyle]=\"{'transform': 'rotateX(' + xRot + 'deg) rotateY(' + yRot + 'deg)'}\">\n <ng-content></ng-content>\n </div>\n</div>\n", styles: [".tilt-container{display:grid;grid-template-columns:1fr;grid-template-rows:1fr;position:relative;perspective:500px;width:-moz-fit-content;width:fit-content}.tilt-content{transform-origin:center;position:relative;transition:transform 1s ease}\n"] }] }], ctorParameters: function () { return []; }, propDecorators: { onMouseOut: [{ type: HostListener, args: ['mouseout'] }], onMouseMove: [{ type: HostListener, args: ['mousemove', ['$event']] }], tiltContentRef: [{ type: ViewChild, args: ['tilt'] }], rotationDegreesX: [{ type: Input }], rotationDegreesY: [{ type: Input }] } }); class NgxFlipCardModule { } NgxFlipCardModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: NgxFlipCardModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); NgxFlipCardModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: NgxFlipCardModule, declarations: [NgxFlipCardComponent, NgxTiltCardComponent], imports: [CommonModule], exports: [NgxFlipCardComponent, NgxTiltCardComponent] }); NgxFlipCardModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: NgxFlipCardModule, imports: [[ CommonModule ]] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: NgxFlipCardModule, decorators: [{ type: NgModule, args: [{ declarations: [ NgxFlipCardComponent, NgxTiltCardComponent ], imports: [ CommonModule ], exports: [ NgxFlipCardComponent, NgxTiltCardComponent ] }] }] }); /** * Generated bundle index. Do not edit. */ export { NgxFlipCardComponent, NgxFlipCardModule, NgxTiltCardComponent }; //# sourceMappingURL=ngx-flip-card.mjs.map