UNPKG

@wizdm/animate

Version:

On Scroll Animation for Angular

1,063 lines (1,026 loc) 48.2 kB
import { InjectionToken, ɵɵdefineInjectable, ɵɵinject, NgZone, Injectable, Optional, Inject, EventEmitter, Component, ElementRef, Renderer2, HostBinding, Input, Output, HostListener, Directive, forwardRef, NgModule } from '@angular/core'; import { switchMap, startWith, map, debounceTime, shareReplay, take, scan, distinctUntilChanged, delay, takeWhile } from 'rxjs/operators'; import { coerceNumberProperty, coerceBooleanProperty } from '@angular/cdk/coercion'; import { BehaviorSubject, of, Observable, Subject } from 'rxjs'; import { transition, style, animate, keyframes, state, trigger } from '@angular/animations'; import { ScrollDispatcher, ViewportRuler, ScrollingModule } from '@angular/cdk/scrolling'; /** Animate config token */ const ANIMATE_CONFIG = new InjectionToken('wizdm.animate.config'); /** Builds the config object checking whenever the Browser support the IntersectionObserver API */ function animateConfigFactory(value) { // Starts with the given mode defaulting to auto detection let triggerMode = value && value.triggerMode || 'auto'; if (triggerMode === 'auto' || triggerMode === 'intersectionObserver') { // Checks for Browser IntersectionObserver support const ioSupported = 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in window.IntersectionObserverEntry.prototype; // Applies the best mode triggerMode = ioSupported ? 'intersectionObserver' : 'scrolling'; } // Ensure to use scrolling otherwise else { triggerMode = 'scrolling'; } // Returns the config object return Object.assign(Object.assign({}, value), { triggerMode }); } class AnimateService { constructor(scroll, viewPort, zone, config) { this.scroll = scroll; this.viewPort = viewPort; this.zone = zone; this.config = config; this.options$ = new BehaviorSubject({}); // Gets the module configuration this.config = animateConfigFactory(config); // Computes a common view observable to support the 'scrolling' triggering method this.view$ = this.options$.pipe( // Tracks for viewport changes giving it 100ms time to accurately update for orientation changes switchMap(options => viewPort.change(100).pipe( // Starts with a value startWith(null), // Gets the viewport map(() => { // Picks the ClientRect of the relevant container const rt = (options.root instanceof Element) ? options.root.getBoundingClientRect() : this.viewPort.getViewportRect(); // Combines the various options to build the final container const left = rt.left + (options.left || this.config.offsetLeft || 0); const top = rt.top + (options.top || this.config.offsetTop || 0); const right = rt.right + (options.right || this.config.offsetRight || 0); const bottom = rt.bottom + (options.bottom || this.config.offsetBottom || 0); // Returns the reultins client rect return { top, left, bottom, right, height: bottom - top, width: right - left }; }), // Debounces to aggregate fast changes (like during orientation changes) debounceTime(20))), // Makes all the component to share the same viewport values shareReplay(1)); } /** True when the trigger is provided using the IntersectionObserver API */ get useIntersectionObserver() { return this.config.triggerMode === 'intersectionObserver'; } /** True when the trigger is provided using cdk/scrolling package */ get useScrolling() { return this.config.triggerMode === 'scrolling'; } /** Applies the given options to the triggering service */ setup(options) { this.options$.next(options); } // Triggers the animation trigger(elm, threshold) { // Waits until the zone is stable once, aka the render is complete so the element to measure is there return source => this.zone.onStable.pipe( // Waits just once take(1), // Triggers the play and replay requests switchMap(() => source), // Triggers upon the most suitable method switchMap(trigger => // Simply return the sourced trigger when threshold is 0 (threshold <= 0) ? of(trigger) : ( // Check upon the configured method otherwise this.useIntersectionObserver ? // Triggers upon element intersection (IntersectionObserver API) this.intersecting(elm, threshold) : // Triggers upon cdk/scrolling this.scrolling(elm, threshold)))); } // Triggers the animation on intersection (using the IntersectionObserver API) intersecting(elm, threshold) { return this.options$.pipe( // Turns the options into a suitable configuration for the IntersectionObserver AnimateOptions map(options => { // Identifies an optional element to be used as the container const root = options.root || null; // Merges the margins from both the global config and the local options const top = options.top || this.config.offsetTop || 0; const right = options.right || this.config.offsetRight || 0; const bottom = options.bottom || this.config.offsetBottom || 0; const left = options.left || this.config.offsetLeft || 0; // Computes the rootMargin string acordingly const rootMargin = `${-top}px ${-right}px ${-bottom}px ${-left}px`; // Returns the proper initialization object return { root, rootMargin }; }), // Observes the element switchMap(options => this.observe(elm, threshold, options))); } /** Builds an Obsevable out of the IntersectionObserver API */ observe(elm, threshold, options) { return new Observable(subscriber => { // Creates a single entry observer const observer = new IntersectionObserver(entries => { // Monitors the only enry intesection ratio const ratio = entries[0].intersectionRatio; // Emits true whenever the intersection cross the threashold (making sure to run in the angular zone) if (ratio >= threshold) { this.zone.run(() => subscriber.next(true)); } // Emits false whenever the intersection cross back to full invisibility (making sure to run in the angular zone) if (ratio <= 0) { this.zone.run(() => subscriber.next(false)); } // Initializes the observer with the given parameters }, Object.assign(Object.assign({}, options), { threshold: [0, threshold] })); // Starts observing the target element observer.observe(elm.nativeElement); // Disconnects when unsubscribed return () => observer.disconnect(); }); } // Triggers the animation on scroll scrolling(elm, threshold) { // Returns an AOS observable using cdk/scrollilng return this.scroll.ancestorScrolled(elm, 0).pipe( // Makes sure triggering the start no matter there's no scroll event hits yet startWith(0), // Maps the scrolling to the element visibility value switchMap(() => this.visibility(elm)), // Applies an hysteresys, so, to trigger the animation on based on the treshold while off on full invisibility scan((result, visiblility) => (visiblility >= threshold) || (result && visiblility > 0), false), // Distincts the resulting triggers distinctUntilChanged(), // Runs within the angular zone to trigger change detection back on source => new Observable(subscriber => source.subscribe(value => this.zone.run(() => subscriber.next(value))))); } // Computes the element's visibility ratio against the container visibility(elm) { // Resolves from the latest viewport return this.view$.pipe(map(view => { // Gets the element's bounding rect const rect = elm && elm.nativeElement && elm.nativeElement.getBoundingClientRect(); if (!rect) { return 0; } // Return 1.0 when the element is fully within the viewport if (rect.left > view.left - 1 && rect.top > view.top - 1 && rect.right < view.right + 1 && rect.bottom < view.bottom + 1) { return 1; } // Computes the intersection area otherwise const a = Math.round(rect.width * rect.height); const b = Math.max(0, Math.min(rect.right, view.right) - Math.max(rect.left, view.left)); const c = Math.max(0, Math.min(rect.bottom, view.bottom) - Math.max(rect.top, view.top)); // Returns the amount of visible area return Math.round(b * c / a * 10) / 10; })); } } /** @nocollapse */ AnimateService.ɵprov = ɵɵdefineInjectable({ factory: function AnimateService_Factory() { return new AnimateService(ɵɵinject(ScrollDispatcher), ɵɵinject(ViewportRuler), ɵɵinject(NgZone), ɵɵinject(ANIMATE_CONFIG, 8)); }, token: AnimateService, providedIn: "root" }); AnimateService.decorators = [ { type: Injectable, args: [{ providedIn: 'root' },] } ]; /** @nocollapse */ AnimateService.ctorParameters = () => [ { type: ScrollDispatcher }, { type: ViewportRuler }, { type: NgZone }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [ANIMATE_CONFIG,] }] } ]; const beat = [ transition('* => beat', [ style('*'), animate('{{timing}} {{delay}} cubic-bezier(.8, -0.6, 0.2, 1.5)', keyframes([ style({ transform: 'scale(0.8)' }), style({ transform: 'scale(1.5)' }), style({ transform: 'scale(1)' }) ])) ], { params: { timing: '500ms', delay: '' } }) ]; const bounce = [ transition('* => bounce', [ style({ transformOrigin: 'center bottom' }), animate('{{timing}} {{delay}} ease-in-out', keyframes([ style({ transform: 'translate3d(0, 0, 0)', animationTimingFunction: 'cubic-bezier(0.215, 0.61, 0.355, 1)', offset: 0 }), style({ transform: 'translate3d(0, 0, 0)', animationTimingFunction: 'cubic-bezier(0.215, 0.61, 0.355, 1)', offset: 0.2 }), style({ transform: 'translate3d(0, -30px, 0)', animationTimingFunction: 'cubic-bezier(0.755, 0.05, 0.855, 0.06)', offset: 0.4 }), style({ transform: 'translate3d(0, -30px, 0)', animationTimingFunction: 'cubic-bezier(0.755, 0.05, 0.855, 0.06', offset: 0.43 }), style({ transform: 'translate3d(0, 0, 0)', animationTimingFunction: 'cubic-bezier(0.215, 0.61, 0.355, 1)', offset: 0.53 }), style({ transform: 'translate3d(0, -15px, 0)', animationTimingFunction: 'cubic-bezier(0.755, 0.05, 0.855, 0.06)', offset: 0.7 }), style({ transform: 'translate3d(0, 0, 0)', animationTimingFunction: 'cubic-bezier(0.215, 0.61, 0.355, 1)', offset: 0.8 }), style({ transform: 'translate3d(0, -4px, 0)', offset: 0.9 }), style({ transform: 'translate3d(0, 0, 0)', animationTimingFunction: 'cubic-bezier(0.215, 0.61, 0.355, 1)', offset: 1 }) ])) ], { params: { timing: '1s', delay: '' } }) ]; const headShake = [ transition('* => headShake', [ style('*'), animate('{{timing}} {{delay}} ease-in-out', keyframes([ style({ transform: 'translateX(0)', offset: 0 }), style({ transform: 'translateX(-6px) rotateY(-9deg)', offset: 0.065 }), style({ transform: 'translateX(5px) rotateY(7deg)', offset: 0.185 }), style({ transform: 'translateX(-3px) rotateY(-5deg)', offset: 0.315 }), style({ transform: 'translateX(2px) rotateY(3deg)', offset: 0.435 }), style({ transform: 'translateX(0)', offset: 0.5 }) ])) ], { params: { timing: '1s', delay: '' } }) ]; const heartBeat = [ transition('* => heartBeat', [ style('*'), animate('{{timing}} {{delay}} ease-in-out', keyframes([ style({ transform: 'scale(1)', offset: 0 }), style({ transform: 'scale(1.3)', offset: 0.14 }), style({ transform: 'scale(1)', offset: 0.28 }), style({ transform: 'scale(1.3)', offset: 0.42 }), style({ transform: 'scale(1)', offset: 0.70 }) ])) ], { params: { timing: '1s', delay: '' } }) ]; const pulse = [ transition('* => pulse', [ style('*'), animate('{{timing}} {{delay}} ease-in-out', keyframes([ style({ transform: 'scale(1)' }), style({ transform: 'scale(1.05)' }), style({ transform: 'scale(1)' }) ])) ], { params: { timing: '500ms', delay: '' } }) ]; const rubberBand = [ transition('* => rubberBand', [ style('*'), animate('{{timing}} {{delay}} ease-in-out', keyframes([ style({ transform: 'scale3d(1, 1, 1)', offset: 0 }), style({ transform: 'scale3d(1.25, 0.75, 1)', offset: 0.3 }), style({ transform: 'scale3d(0.75, 1.25, 1)', offset: 0.4 }), style({ transform: 'scale3d(1.15, 0.85, 1)', offset: 0.5 }), style({ transform: 'scale3d(0.95, 1.05, 1)', offset: 0.65 }), style({ transform: 'scale3d(1.05, 0.95, 1)', offset: 0.75 }), style({ transform: 'scale3d(1, 1, 1)', offset: 1 }), ])) ], { params: { timing: '1s', delay: '' } }) ]; const shake = [ transition('* => shake', [ style('*'), animate('{{timing}} {{delay}} ease-in-out', keyframes([ style({ transform: 'translateX(0)', offset: 0 }), style({ transform: 'translateX(-10px)', offset: 0.1 }), style({ transform: 'translateX(10px)', offset: 0.2 }), style({ transform: 'translateX(-10px)', offset: 0.3 }), style({ transform: 'translateX(10px)', offset: 0.4 }), style({ transform: 'translateX(-10px)', offset: 0.5 }), style({ transform: 'translateX(10px)', offset: 0.6 }), style({ transform: 'translateX(-10px)', offset: 0.7 }), style({ transform: 'translateX(10px)', offset: 0.8 }), style({ transform: 'translateX(-10px)', offset: 0.9 }), style({ transform: 'translateX(0)', offset: 1 }) ])) ], { params: { timing: '1s', delay: '' } }) ]; const swing = [ transition('* => swing', [ style({ transformOrigin: 'top center' }), animate('{{timing}} {{delay}} ease-in-out', keyframes([ style({ transform: 'rotate3d(0, 0, 1, 0deg)', offset: 0 }), style({ transform: 'rotate3d(0, 0, 1, 15deg)', offset: 0.2 }), style({ transform: 'rotate3d(0, 0, 1, -10deg)', offset: 0.4 }), style({ transform: 'rotate3d(0, 0, 1, 5deg)', offset: 0.6 }), style({ transform: 'rotate3d(0, 0, 1, -5deg)', offset: 0.8 }), style({ transform: 'rotate3d(0, 0, 1, 0deg)', offset: 1 }) ])) ], { params: { timing: '1s', delay: '' } }) ]; const wobble = [ transition('* => wobble', [ style('*'), animate('{{timing}} {{delay}} ease-in-out', keyframes([ style({ transform: 'translateX(0)', offset: 0 }), style({ transform: 'translateX(-25%) rotate3d(0, 0, 1, -5deg)', offset: 0.15 }), style({ transform: 'translateX(20%) rotate3d(0, 0, 1, 3deg)', offset: 0.3 }), style({ transform: 'translateX(-15%) rotate3d(0, 0, 1, -3deg)', offset: 0.45 }), style({ transform: 'translateX(10%) rotate3d(0, 0, 1, 2deg)', offset: 0.6 }), style({ transform: 'translateX(-5%) rotate3d(0, 0, 1, -1deg)', offset: 0.75 }), style({ transform: 'translateX(0)', offset: 1 }) ])) ], { params: { timing: '1s', delay: '' } }) ]; const jello = [ transition('* => jello', [ style({ transformOrigin: 'center' }), animate('{{timing}} {{delay}} ease-in-out', keyframes([ style({ transform: 'skewX(0) skewY(0)', offset: 0 }), style({ transform: 'skewX(0) skewY(0)', offset: 0.111 }), style({ transform: 'skewX(-12.5) skewY(-12.5)', offset: 0.222 }), style({ transform: 'skewX(6.25deg) skewY(6.25deg)', offset: 0.333 }), style({ transform: 'skewX(-3.125deg) skewY(-3.125deg)', offset: 0.444 }), style({ transform: 'skewX(1.5625deg) skewY(1.5625deg)', offset: 0.555 }), style({ transform: 'skewX(-0.78125deg) skewY(-0.78125deg)', offset: 0.666 }), style({ transform: 'skewX(0.390625deg) skewY(0.390625deg)', offset: 0.777 }), style({ transform: 'skewX(-0.1953125deg) skewY(-0.1953125deg)', offset: 0.888 }), style({ transform: 'skewX(0) skewY(0)', offset: 1 }) ])) ], { params: { timing: '1s', delay: '' } }) ]; const tada = [ transition('* => tada', [ style('*'), animate('{{timing}} {{delay}} ease-in-out', keyframes([ style({ transform: 'scale3d(1, 1, 1)', offset: 0 }), style({ transform: 'scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg)', offset: 0.1 }), style({ transform: 'scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg)', offset: 0.2 }), style({ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.3 }), style({ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.4 }), style({ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.5 }), style({ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.6 }), style({ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.7 }), style({ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg)', offset: 0.8 }), style({ transform: 'scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg)', offset: 0.9 }), style({ transform: 'scale3d(1, 1, 1)', offset: 1 }), ])) ], { params: { timing: '1s', delay: '' } }) ]; const flip = [ transition('* => flip', [ style({ backfaceVisibility: 'visible' }), animate('{{timing}} {{delay}} ease-in-out', keyframes([ style({ transform: 'perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, -360deg)', animationTimingFunction: 'ease-out', offset: 0 }), style({ transform: ' perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg)', animationTimingFunction: 'ease-out', offset: 0.4 }), style({ transform: 'perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg)', animationTimingFunction: 'ease-in', offset: 0.5 }), style({ transform: 'perspective(400px) scale3d(0.95, 0.95, 0.95) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg)', animationTimingFunction: 'ease-in', offset: 0.8 }), style({ transform: 'perspective(400px) scale3d(1, 1, 1) translate3d(0, 0, 0) rotate3d(0, 1, 0, 0deg)', animationTimingFunction: 'ease-in', offset: 1 }) ])) ], { params: { timing: '1s', delay: '' } }) ]; const bumpIn = [ state('idle-bumpIn', style({ opacity: 0 })), transition('* => bumpIn', [ style({ transform: 'scale(0.5)', opacity: 0 }), animate("{{timing}} {{delay}} cubic-bezier(.8, -0.6, 0.2, 1.5)", style({ transform: 'scale(1)', opacity: 1 })) ], { params: { timing: '500ms', delay: '' } }) ]; const bounceIn = [ state('idle-bounceIn', style({ opacity: 0 })), state('idle-bounceInDown', style({ opacity: 0 })), state('idle-bounceInLeft', style({ opacity: 0 })), state('idle-bounceInUp', style({ opacity: 0 })), state('idle-bounceInRight', style({ opacity: 0 })), transition('* => bounceIn', animate('{{timing}} {{delay}} cubic-bezier(0.215, 0.61, 0.355, 1)', keyframes([ style({ transform: 'scale(0.3)', opacity: 0, offset: 0 }), style({ transform: 'scale(1.1)', offset: 0.2 }), style({ transform: 'scale(0.9)', offset: 0.4 }), style({ transform: 'scale(1.03)', opacity: 1, offset: 0.6 }), style({ transform: 'scale(0.97)', offset: 0.8 }), style({ transform: 'scale(1)', opacity: 1, offset: 1 }) ])), { params: { timing: '750ms', delay: '' } }), transition('* => bounceInDown', animate('{{timing}} {{delay}} cubic-bezier(0.215, 0.61, 0.355, 1)', keyframes([ style({ opacity: 0, transform: 'translateY(-100%)', offset: 0 }), style({ opacity: 1, transform: 'translateY(25px)', offset: 0.6 }), style({ transform: 'translateY(-10px)', offset: 0.75 }), style({ transform: 'translateY(5px)', offset: 0.9 }), style({ opacity: 1, transform: 'translateY(0)', offset: 1 }) ])), { params: { timing: '1s', delay: '' } }), transition('* => bounceInLeft', animate('{{timing}} {{delay}} cubic-bezier(0.215, 0.61, 0.355, 1)', keyframes([ style({ opacity: 0, transform: 'translateX(-100%)', offset: 0 }), style({ opacity: 1, transform: 'translateX(25px)', offset: 0.6 }), style({ transform: 'translateX(-10px)', offset: 0.75 }), style({ transform: 'translateX(5px)', offset: 0.9 }), style({ opacity: 1, transform: 'translateX(0)', offset: 1 }) ])), { params: { timing: '1s', delay: '' } }), transition('* => bounceInUp', animate('{{timing}} {{delay}} cubic-bezier(0.215, 0.61, 0.355, 1)', keyframes([ style({ opacity: 0, transform: 'translateY(100%)', offset: 0 }), style({ opacity: 1, transform: 'translateY(-25px)', offset: 0.6 }), style({ transform: 'translateY(10px)', offset: 0.75 }), style({ transform: 'translateY(-5px)', offset: 0.9 }), style({ opacity: 1, transform: 'translateY(0)', offset: 1 }) ])), { params: { timing: '1s', delay: '' } }), transition('* => bounceInRight', animate('{{timing}} {{delay}} cubic-bezier(0.215, 0.61, 0.355, 1)', keyframes([ style({ opacity: 0, transform: 'translateX(100%)', offset: 0 }), style({ opacity: 1, transform: 'translateX(-25px)', offset: 0.6 }), style({ transform: 'translateX(10px)', offset: 0.75 }), style({ transform: 'translateX(-5px)', offset: 0.9 }), style({ opacity: 1, transform: 'translateX(0)', offset: 1 }) ])), { params: { timing: '1s', delay: '' } }) ]; const fadeIn = [ state('idle-fadeIn', style({ opacity: 0 })), state('idle-fadeInRight', style({ opacity: 0 })), state('idle-fadeInLeft', style({ opacity: 0 })), state('idle-fadeInUp', style({ opacity: 0 })), state('idle-fadeInDown', style({ opacity: 0 })), transition('* => fadeIn', [ style({ opacity: 0 }), animate('{{timing}} {{delay}} ease-in', style('*')) ], { params: { timing: '1s', delay: '' } }), transition('* => fadeInRight', [ style({ opacity: 0, transform: 'translateX(-20px)' }), animate('{{timing}} {{delay}} ease-in', style('*')) ], { params: { timing: '1s', delay: '' } }), transition('* => fadeInLeft', [ style({ opacity: 0, transform: 'translateX(20px)' }), animate('{{timing}} {{delay}} ease-in', style('*')) ], { params: { timing: '1s', delay: '' } }), transition('* => fadeInUp', [ style({ opacity: 0, transform: 'translateY(20px)' }), animate('{{timing}} {{delay}} ease-in', style('*')) ], { params: { timing: '1s', delay: '' } }), transition('* => fadeInDown', [ style({ opacity: 0, transform: 'translateY(-20px)' }), animate('{{timing}} {{delay}} ease-in', style('*')) ], { params: { timing: '1s', delay: '' } }), ]; const flipIn = [ state('idle-flipInX', style({ opacity: 0 })), state('idle-flipInY', style({ opacity: 0 })), transition('* => flipInX', [ style({ backfaceVisibility: 'visible' }), animate('{{timing}} {{delay}} ease-in', keyframes([ style({ transform: 'perspective(400px) rotate3d(1, 0, 0, 90deg)', opacity: 0, offset: 0 }), style({ transform: ' perspective(400px) rotate3d(1, 0, 0, -20deg)', opacity: 1, offset: 0.4 }), style({ transform: 'perspective(400px) rotate3d(1, 0, 0, 10deg)', offset: 0.6 }), style({ transform: 'perspective(400px) rotate3d(1, 0, 0, -5deg)', offset: 0.8 }), style({ transform: 'perspective(400px) rotate3d(1, 0, 0, 0)', offset: 1 }) ])) ], { params: { timing: '1s', delay: '' } }), transition('* => flipInY', [ style({ backfaceVisibility: 'visible' }), animate('{{timing}} {{delay}} ease-in', keyframes([ style({ transform: 'perspective(400px) rotate3d(0, 1, 0, 90deg)', opacity: 0, offset: 0 }), style({ transform: ' perspective(400px) rotate3d(0, 1, 0, -20deg)', opacity: 1, offset: 0.4 }), style({ transform: 'perspective(400px) rotate3d(0, 1, 0, 10deg)', offset: 0.6 }), style({ transform: 'perspective(400px) rotate3d(0, 1, 0, -5deg)', offset: 0.8 }), style({ transform: 'perspective(400px) rotate3d(0, 1, 0, 0)', offset: 1 }) ])) ], { params: { timing: '1s', delay: '' } }) ]; const jackInTheBox = [ state('idle-jackInTheBox', style({ opacity: 0 })), transition('* => jackInTheBox', [ style('*'), animate('{{timing}} {{delay}} ease-in', keyframes([ style({ transform: 'scale(0.1) rotate(30deg)', transformOrigin: 'center bottom', opacity: 0, offset: 0 }), style({ transform: 'rotate(-10deg)', opacity: 0.7, offset: 0.5 }), style({ transform: 'rotate(3deg)', offset: 0.7 }), style({ transform: 'scale(1)', opacity: 1, offset: 1 }) ])) ], { params: { timing: '1s', delay: '' } }) ]; /*@keyframes jackInTheBox { from { opacity: 0; transform: scale(0.1) rotate(30deg); transform-origin: center bottom; } 50% { transform: rotate(-10deg); } 70% { transform: rotate(3deg); } to { opacity: 1; transform: scale(1); } } .jackInTheBox { animation-name: jackInTheBox; }*/ const landing = [ state('idle-landing', style({ opacity: 0 })), transition('* => landing', [ style({ transform: 'scale(1.2)', opacity: 0 }), animate('{{timing}} {{delay}} ease', style('*')) ], { params: { timing: '2s', delay: '' } }) ]; const rollIn = [ state('idle-rollIn', style({ opacity: 0 })), transition('* => rollIn', [ style({ transform: 'translateX(-100%) rotate3d(0, 0, 1, -120deg)', opacity: 0 }), animate("{{timing}} {{delay}} cubic-bezier(.8, -0.6, 0.2, 1.5)", style({ transform: 'translateX(0)', opacity: 1 })) ], { params: { timing: '1s', delay: '' } }) ]; const zoomIn = [ // Idle states state('idle-zoomIn', style({ opacity: 0 })), state('idle-zoomInDown', style({ opacity: 0 })), state('idle-zoomInLeft', style({ opacity: 0 })), state('idle-zoomInUp', style({ opacity: 0 })), state('idle-zoomInRight', style({ opacity: 0 })), transition('* => zoomIn', animate('{{timing}} {{delay}} ease-in', keyframes([ style({ opacity: 0, transform: 'scale(0.3)' }), style({ opacity: 1, transform: 'scale(0.65)' }), style({ opacity: 1, transform: 'scale(1)' }) ])), { params: { timing: '1s', delay: '' } }), transition('* => zoomInDown', animate('{{timing}} {{delay}} ease-in', keyframes([ style({ opacity: 0, transform: 'scale(0.1) translateY(-1000px)', animationTimingFunction: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)', offset: 0 }), style({ opacity: 1, transform: 'scale(0.475) translateY(60px)', animationTimingFunction: 'cubic-bezier(0.175, 0.885, 0.32, 1)', offset: 0.6 }), style({ opacity: 1, transform: 'scale(1) translateY(0)', offset: 1 }) ])), { params: { timing: '1s', delay: '' } }), transition('* => zoomInLeft', animate('{{timing}} {{delay}} ease-in', keyframes([ style({ opacity: 0, transform: 'scale(0.1) translateX(-1000px)', animationTimingFunction: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)', offset: 0 }), style({ opacity: 1, transform: 'scale(0.475) translateX(60px)', animationTimingFunction: 'cubic-bezier(0.175, 0.885, 0.32, 1)', offset: 0.6 }), style({ opacity: 1, transform: 'scale(1) translateX(0)', offset: 1 }) ])), { params: { timing: '1s', delay: '' } }), transition('* => zoomInUp', animate('{{timing}} {{delay}} ease-in', keyframes([ style({ opacity: 0, transform: 'scale(0.1) translateY(1000px)', animationTimingFunction: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)', offset: 0 }), style({ opacity: 1, transform: 'scale(0.475) translateY(-60px)', animationTimingFunction: 'cubic-bezier(0.175, 0.885, 0.32, 1)', offset: 0.6 }), style({ opacity: 1, transform: 'scale(1) translateY(0)', offset: 1 }) ])), { params: { timing: '1s', delay: '' } }), transition('* => zoomInRight', animate('{{timing}} {{delay}} ease-in', keyframes([ style({ opacity: 0, transform: 'scale(0.1) translateX(1000px)', animationTimingFunction: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)', offset: 0 }), style({ opacity: 1, transform: 'scale(0.475) translateX(-60px)', animationTimingFunction: 'cubic-bezier(0.175, 0.885, 0.32, 1)', offset: 0.6 }), style({ opacity: 1, transform: 'scale(1) translateX(0)', offset: 1 }) ])), { params: { timing: '1s', delay: '' } }) ]; const bounceOut = [ // Ending states state('bounceOut', style({ opacity: 0 })), state('bounceOutDown', style({ opacity: 0 })), state('bounceOutUp', style({ opacity: 0 })), state('bounceOutRight', style({ opacity: 0 })), state('bounceOutLeft', style({ opacity: 0 })), // Transitions transition('* => bounceOut', [ style({ opacity: 1 }), animate('{{timing}} {{delay}} ease-out', keyframes([ style({ transform: 'scale(0.9)', offset: 0.2 }), style({ transform: 'scale(1.1)', offset: 0.5 }), style({ transform: 'scale(1.1)', offset: 0.55 }), style({ opacity: 0, transform: 'scale(0.3)', offset: 1 }) ])) ], { params: { timing: '750ms', delay: '' } }), transition('* => bounceOutDown', [ style({ opacity: 1 }), animate('{{timing}} {{delay}} ease-out', keyframes([ style({ transform: 'translateY(10px)', offset: 0.2 }), style({ transform: 'translateY(-20px)', offset: 0.4 }), style({ transform: 'translateY(-20px)', offset: 0.45 }), style({ opacity: 0, transform: 'translateY(2000px)', offset: 1 }) ])) ], { params: { timing: '1s', delay: '' } }), transition('* => bounceOutUp', [ style({ opacity: 1 }), animate('{{timing}} {{delay}} ease-out', keyframes([ style({ transform: 'translateY(-10px)', offset: 0.2 }), style({ transform: 'translateY(20px)', offset: 0.4 }), style({ transform: 'translateY(20px)', offset: 0.45 }), style({ opacity: 0, transform: 'translateY(-2000px)', offset: 1 }) ])) ], { params: { timing: '1s', delay: '' } }), transition('* => bounceOutRight', [ style({ opacity: 1 }), animate('{{timing}} {{delay}} ease-out', keyframes([ style({ transform: 'translateX(-20px)', offset: 0.2 }), style({ opacity: 0, transform: 'translateX(2000px)', offset: 1 }) ])) ], { params: { timing: '1s', delay: '' } }), transition('* => bounceOutLeft', [ style({ opacity: 1 }), animate('{{timing}} {{delay}} ease-out', keyframes([ style({ transform: 'translateX(20px)', offset: 0.2 }), style({ opacity: 0, transform: 'translateX(-2000px)', offset: 1 }) ])) ], { params: { timing: '1s', delay: '' } }) ]; const fadeOut = [ // Ending states state('fadeOut', style({ opacity: 0 })), state('fadeOutRight', style({ opacity: 0 })), state('fadeOutLeft', style({ opacity: 0 })), state('fadeOutDown', style({ opacity: 0 })), state('fadeOutUp', style({ opacity: 0 })), // Transitions transition('* => fadeOut', [ animate('{{timing}} {{delay}} ease-out', style({ opacity: 0 })) ], { params: { timing: '1s', delay: '' } }), transition('* => fadeOutRight', [ animate('{{timing}} {{delay}} ease-out', style({ opacity: 0, transform: 'translateX(20px)' })) ], { params: { timing: '1s', delay: '' } }), transition('* => fadeOutLeft', [ animate('{{timing}} {{delay}} ease-out', style({ opacity: 0, transform: 'translateX(-20px)' })) ], { params: { timing: '1s', delay: '' } }), transition('* => fadeOutDown', [ animate('{{timing}} {{delay}} ease-out', style({ opacity: 0, transform: 'translateY(20px)' })) ], { params: { timing: '1s', delay: '' } }), transition('* => fadeOutUp', [ animate('{{timing}} {{delay}} ease-out', style({ opacity: 0, transform: 'translateY(-20px)' })) ], { params: { timing: '1s', delay: '' } }) ]; const hinge = [ state('hinge', style({ opacity: 0 })), transition('* => hinge', [ style({ transformOrigin: 'top left' }), animate('{{timing}} {{delay}} ease-in-out', keyframes([ style({ transform: 'rotate3d(0, 0, 1, 0', offset: 0 }), style({ transform: 'rotate3d(0, 0, 1, 80deg)', offset: 0.2 }), style({ transform: 'rotate3d(0, 0, 1, 60deg)', offset: 0.4 }), style({ transform: 'rotate3d(0, 0, 1, 80deg)', offset: 0.6 }), style({ transform: 'rotate3d(0, 0, 1, 60deg)', offset: 0.8 }), style({ transform: 'translateY(700px)', offset: 1 }) ])) ], { params: { timing: '2s', delay: '' } }) ]; const rollOut = [ state('rollOut', style({ opacity: 0 })), transition('* => rollOut', [ style({ transform: 'translateX(0)', opacity: 1 }), animate("{{timing}} {{delay}} cubic-bezier(.8, -0.6, 0.2, 1.5)", style({ transform: 'translateX(100%) rotate3d(0, 0, 1, 120deg)', opacity: 0 })) ], { params: { timing: '1s', delay: '' } }) ]; const zoomOut = [ // Ending states state('zoomOut', style({ opacity: 0 })), state('zoomOutDown', style({ opacity: 0 })), state('zoomOutRight', style({ opacity: 0 })), state('zoomOutUp', style({ opacity: 0 })), state('zoomOutLeft', style({ opacity: 0 })), transition('* => zoomOut', animate('{{timing}} {{delay}} ease-out', keyframes([ style({ opacity: 1, transform: 'scale(1)' }), style({ opacity: 0, transform: 'scale(0.3)' }), style({ opacity: 0, transform: 'scale(0.3)' }) ])), { params: { timing: '1s', delay: '' } }), transition('* => zoomOutDown', animate('{{timing}} {{delay}} ease-in', keyframes([ style({ opacity: 0, transform: 'scale(0.475) translateY(-60px)', animationTimingFunction: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)', offset: 0 }), style({ opacity: 1, transform: 'scale(0.1) translateY(2000px)', transformOrigin: 'center bottom', animationTimingFunction: 'ubic-bezier(0.175, 0.885, 0.32, 1)', offset: 0.6 }), style({ opacity: 1, transform: 'scale(1) translateY(0)', offset: 1 }) ])), { params: { timing: '1s', delay: '' } }), transition('* => zoomOutRight', animate('{{timing}} {{delay}} ease-in', keyframes([ style({ opacity: 0, transform: 'scale(0.475) translateX(-42px)', animationTimingFunction: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)', offset: 0 }), style({ opacity: 1, transform: 'scale(0.1) translateX(2000px)', transformOrigin: 'center bottom', animationTimingFunction: 'ubic-bezier(0.175, 0.885, 0.32, 1)', offset: 0.6 }), style({ opacity: 1, transform: 'scale(1) translateX(0)', offset: 1 }) ])), { params: { timing: '1s', delay: '' } }), transition('* => zoomOutUp', animate('{{timing}} {{delay}} ease-in', keyframes([ style({ opacity: 0, transform: 'scale(0.475) translateY(60px)', animationTimingFunction: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)', offset: 0 }), style({ opacity: 1, transform: 'scale(0.1) translateY(-2000px)', transformOrigin: 'center bottom', animationTimingFunction: 'ubic-bezier(0.175, 0.885, 0.32, 1)', offset: 0.6 }), style({ opacity: 1, transform: 'scale(1) translateY(0)', offset: 1 }) ])), { params: { timing: '1s', delay: '' } }), transition('* => zoomOutLeft', animate('{{timing}} {{delay}} ease-in', keyframes([ style({ opacity: 0, transform: 'scale(0.475) translateX(42px)', animationTimingFunction: 'cubic-bezier(0.55, 0.055, 0.675, 0.19)', offset: 0 }), style({ opacity: 1, transform: 'scale(0.1) translateX(-2000px)', transformOrigin: 'center bottom', animationTimingFunction: 'ubic-bezier(0.175, 0.885, 0.32, 1)', offset: 0.6 }), style({ opacity: 1, transform: 'scale(1) translateX(0)', offset: 1 }) ])), { params: { timing: '1s', delay: '' } }) ]; class AnimateComponent { constructor(elm, scroll, renderer) { this.elm = elm; this.scroll = scroll; this.renderer = renderer; this.replay$ = new Subject(); // Animating properties this.animating = false; this.animated = false; this.disabled = false; /** Emits at the end of the animation */ this.start = new EventEmitter(); /** Emits at the end of the animation */ this.done = new EventEmitter(); this.paused = false; this.threshold = 0; this.once = false; } get idle() { return { value: `idle-${this.animate}` }; } get play() { const params = {}; // Builds the params object, so, leaving to the default values when undefined if (!!this.timing) { params['timing'] = this.timing; } if (!!this.delay) { params['delay'] = this.delay; } return { value: this.animate, params }; } /** Speeds up or slows down the animation */ set speed(speed) { // Turns the requested speed into a valid timing this.timing = { slower: '3s', slow: '2s', normal: '1s', fast: '500ms', faster: '300ms' }[speed || 'normal'] || '1s'; } /** Delays the animation */ set postpone(delay) { // Coerces the input into a number first const value = coerceNumberProperty(delay, 0); if (value) { // Turns a valid number into a ms delay this.delay = `${value}ms`; } else { // Test the string for a valid delay combination this.delay = /^\d+(?:ms|s)$/.test(delay) ? delay : ''; } } /** Disables the animation */ set disableAnimation(value) { this.disabled = coerceBooleanProperty(value); } animationStart() { this.animating = true; this.animated = false; this.start.emit(); } animationDone() { this.animating = false; this.animated = true; this.done.emit(); /** * Removes spurious 'animation' style from the element once done with the animation. * This behaviour has been observed when running on iOS devices where for some reason * the animation engine do not properly clean-up the animation style using cubic-bezier() * as its timing function. The issue do not appear with ease-in/out and others. * */ this.renderer.removeStyle(this.elm.nativeElement, 'animation'); } /** When true, keeps the animation idle until the next replay triggers */ set pauseAnimation(value) { this.paused = coerceBooleanProperty(value); } /** When defined, triggers the animation on element scrolling in the viewport by the specified amount. Amount defaults to 50% when not specified */ set enableAOS(value) { this.threshold = coerceNumberProperty(value, 0.5); } /** When true, triggers the animation on element scrolling in the viewport */ set aosOnce(value) { this.once = coerceBooleanProperty(value); } /** Replays the animation */ set replay(replay) { // Re-triggers the animation again on request (skipping the very fist value) if (!!this.trigger && coerceBooleanProperty(replay)) { this.trigger = this.idle; this.replay$.next(true); } } ngOnInit() { // Triggers the animation based on the input flags this.sub = this.replay$.pipe( // Waits the next round to re-trigger delay(0), // Triggers immediately when not paused startWith(!this.paused), // Builds the AOS observable from the common service this.scroll.trigger(this.elm, this.threshold), // Stop taking the first on trigger when aosOnce is set takeWhile(trigger => !trigger || !this.once, true), // Maps the trigger into animation states map(trigger => trigger ? this.play : this.idle), // Always start with idle startWith(this.idle), // Eliminates multiple triggers distinctUntilChanged()).subscribe(trigger => this.trigger = trigger); } // Disposes of the observable ngOnDestroy() { this.sub.unsubscribe(); } } AnimateComponent.decorators = [ { type: Component, args: [{ selector: '[wmAnimate]', template: '<ng-content></ng-content>', animations: [trigger('animate', [ // Attention seekers ...beat, ...bounce, ...flip, ...headShake, ...heartBeat, ...jello, ...pulse, ...rubberBand, ...shake, ...swing, ...tada, ...wobble, // Entrances ...bumpIn, ...bounceIn, ...fadeIn, ...flipIn, ...jackInTheBox, ...landing, ...rollIn, ...zoomIn, // Exits ...bounceOut, ...fadeOut, ...hinge, ...rollOut, ...zoomOut, // None state('none', style('*')), state('idle-none', style('*')) ])] },] } ]; /** @nocollapse */ AnimateComponent.ctorParameters = () => [ { type: ElementRef }, { type: AnimateService }, { type: Renderer2 } ]; AnimateComponent.propDecorators = { trigger: [{ type: HostBinding, args: ['@animate',] }], animate: [{ type: Input, args: ['wmAnimate',] }], speed: [{ type: Input }], postpone: [{ type: Input, args: ['delay',] }], disableAnimation: [{ type: Input, args: ['disabled',] }], disabled: [{ type: HostBinding, args: ['@.disabled',] }], start: [{ type: Output }], animationStart: [{ type: HostListener, args: ['@animate.start',] }], done: [{ type: Output }], animationDone: [{ type: HostListener, args: ['@animate.done',] }], pauseAnimation: [{ type: Input, args: ['paused',] }], enableAOS: [{ type: Input, args: ['aos',] }], aosOnce: [{ type: Input, args: ['once',] }], replay: [{ type: Input }] }; class AnimateDirective extends AnimateService { constructor(elref, scroll, port, zone, config) { // Constructs the parent AnimateService super(scroll, port, zone, config); this.elref = elref; /** When true instructs the directive to use the element's bounding rect as the animation view */ this.useElement = false; } // Updates the AnimateService options on changes ngOnChanges(changes) { super.setup({ // Uses the host element as the container, when requested root: coerceBooleanProperty(this.useElement) ? this.elref.nativeElement : null, // Computes the optional offsets top: coerceNumberProperty(this.top, 0), right: coerceNumberProperty(this.right, 0), bottom: coerceNumberProperty(this.bottom, 0), left: coerceNumberProperty(this.left, 0) }); } } /** @nocollapse */ AnimateDirective.ɵprov = ɵɵdefineInjectable({ factory: function AnimateDirective_Factory() { return new AnimateDirective(ɵɵinject(ElementRef), ɵɵinject(ScrollDispatcher), ɵɵinject(ViewportRuler), ɵɵinject(NgZone), ɵɵinject(ANIMATE_CONFIG, 8)); }, token: AnimateDirective, providedIn: "root" }); AnimateDirective.decorators = [ { type: Directive, args: [{ selector: '[wmAnimateView]', providers: [ // Provides the AnimateDirective as the service, so, for the children components to trigger within a modified viewport { provide: AnimateService, useExisting: forwardRef(() => AnimateDirective) }, ] },] } ]; /** @nocollapse */ AnimateDirective.ctorParameters = () => [ { type: ElementRef }, { type: ScrollDispatcher }, { type: ViewportRuler }, { type: NgZone }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [ANIMATE_CONFIG,] }] } ]; AnimateDirective.propDecorators = { useElement: [{ type: Input }], top: [{ type: Input }], left: [{ type: Input }], bottom: [{ type: Input }], right: [{ type: Input }] }; class AnimateModule { static init(config) { return { ngModule: AnimateModule, providers: [ { provide: ANIMATE_CONFIG, useValue: config } ] }; } } AnimateModule.decorators = [ { type: NgModule, args: [{ imports: [ScrollingModule], declarations: [AnimateComponent, AnimateDirective], exports: [AnimateComponent, AnimateDirective] },] } ]; ; /** * Generated bundle index. Do not edit. */ export { AnimateComponent, AnimateDirective, AnimateModule, AnimateService, beat as ɵa, bounce as ɵb, ANIMATE_CONFIG as ɵba, flip as ɵc, headShake as ɵd, heartBeat as ɵe, jello as ɵf, pulse as ɵg, rubberBand as ɵh, shake as ɵi, swing as ɵj, tada as ɵk, wobble as ɵl, bumpIn as ɵm, bounceIn as ɵn, fadeIn as ɵo, flipIn as ɵp, jackInTheBox as ɵq, landing as ɵr, rollIn as ɵs, zoomIn as ɵt, bounceOut as ɵu, fadeOut as ɵv, hinge as ɵw, rollOut as ɵx, zoomOut as ɵy }; //# sourceMappingURL=wizdm-animate.js.map