@wizdm/animate
Version:
On Scroll Animation for Angular
1,063 lines (1,026 loc) • 48.2 kB
JavaScript
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