diff --git a/projects/core/src/lib/animations/animations.ts b/projects/core/src/lib/animations/animations.ts index e69de29b..d3a2e9f2 100644 --- a/projects/core/src/lib/animations/animations.ts +++ b/projects/core/src/lib/animations/animations.ts @@ -0,0 +1,13 @@ +import { animation, animate, style } from '@angular/animations'; + +export const fade = animation([ + style({ + opacity: '{{ fromOpacity }}' + }), + animate( + '{{ time }}', + style({ + opacity: '{{ toOpacity }}' + }) + ) +]); diff --git a/projects/core/src/lib/animations/easing-functions.ts b/projects/core/src/lib/animations/easing-functions.ts new file mode 100644 index 00000000..5b141b82 --- /dev/null +++ b/projects/core/src/lib/animations/easing-functions.ts @@ -0,0 +1,50 @@ +export const linear = (t, from, to, duration) => { + const c = to - from; + return (c * t) / duration + from; +}; + +export const easeInQuad = (t, from, to, duration) => { + const c = to - from; + return c * (t /= duration) * t + from; +}; + +export const easeOutSine = (t, from, to, duration) => { + const c = to - from; + return c * Math.sin((t / duration) * (Math.PI / 2)) + from; +}; + +export const easeInOutQuad = (t, from, to, duration) => { + const c = to - from; + if ((t /= duration / 2) < 1) { + return (c / 2) * t * t + from; + } else { + return (-c / 2) * (--t * (t - 2) - 1) + from; + } +}; + +export const easeOutElastic = (t, b, _c, d) => { + let c = _c - b; + let a, p, s; + s = 1.20158; + p = 0; + a = c; + if (t === 0) { + return b; + } else if ((t /= d) === 1) { + return b + c; + } + if (!p) { + p = d * 0.54; + } + if (a < Math.abs(c)) { + a = c; + s = p / 4; + } else { + s = (p / (2 * Math.PI)) * Math.asin(c / a); + } + return ( + a * Math.pow(2, -10 * t) * Math.sin(((t * d - s) * (2 * Math.PI)) / p) + + c + + b + ); +}; diff --git a/projects/core/src/lib/animations/tween.ts b/projects/core/src/lib/animations/tween.ts index d0610147..3d1b2738 100644 --- a/projects/core/src/lib/animations/tween.ts +++ b/projects/core/src/lib/animations/tween.ts @@ -1,6 +1,6 @@ import { Observable } from 'rxjs'; import { ElementRef } from '@angular/core'; -import { tap } from 'rxjs/operators'; +import { tap, map } from 'rxjs/operators'; export function tween( easingFunction: Function, @@ -33,10 +33,14 @@ export const fromTo = ( el: ElementRef, style: string, from: number, - to: number + to: number, + m?: (t: number) => string ) => (source: Observable) => source.pipe( - tap(t => (el.nativeElement.style[style] = t * (to - from) + from + 'px')) + tap(t => { + const te = t * (to - from) + from; + el.nativeElement.style[style] = m ? m(te) : te + 'px'; + }) ); export const morph = (fromEl: ElementRef, toEl: ElementRef) => ( @@ -52,7 +56,7 @@ export const morph = (fromEl: ElementRef, toEl: ElementRef) => ( ); }; -export function getPosition(el: ElementRef) { +export function getPosition(el: ElementRef): RectPosition { const bounds = el.nativeElement.getBoundingClientRect(); return { top: bounds.top, @@ -61,3 +65,17 @@ export function getPosition(el: ElementRef) { width: el.nativeElement.clientWidth }; } +export function setPosition(el: ElementRef, r: RectPosition): ElementRef { + el.nativeElement.style.top = r.top + 'px'; + el.nativeElement.style.left = r.left + 'px'; + el.nativeElement.style.height = r.height + 'px'; + el.nativeElement.style.width = r.width + 'px'; + return el; +} + +export interface RectPosition { + top: number; + left: number; + height: number; + width: number; +} diff --git a/projects/core/src/lib/fab/fab.component.ts b/projects/core/src/lib/fab/fab.component.ts index 69da9a1c..1c84a107 100644 --- a/projects/core/src/lib/fab/fab.component.ts +++ b/projects/core/src/lib/fab/fab.component.ts @@ -1,4 +1,3 @@ -import { AnimationParams } from './../gallery/gallery-image/gallery-image.component'; import { FivFeatureDiscovery } from './../feature-discovery/feature-discovery.component'; import { FivLoadingProgressBar } from './../loading-progress-bar/loading-progress-bar.component'; import { FivIcon } from './../icon/icon.component'; diff --git a/projects/core/src/lib/gallery/gallery-image/gallery-image.component.html b/projects/core/src/lib/gallery/gallery-image/gallery-image.component.html index 95cb00b0..4cf4f9cf 100644 --- a/projects/core/src/lib/gallery/gallery-image/gallery-image.component.html +++ b/projects/core/src/lib/gallery/gallery-image/gallery-image.component.html @@ -1,15 +1,6 @@ - + - - - - -
-
- - - -
\ No newline at end of file + + + \ No newline at end of file diff --git a/projects/core/src/lib/gallery/gallery-image/gallery-image.component.scss b/projects/core/src/lib/gallery/gallery-image/gallery-image.component.scss index 33fa40eb..a9ed03af 100644 --- a/projects/core/src/lib/gallery/gallery-image/gallery-image.component.scss +++ b/projects/core/src/lib/gallery/gallery-image/gallery-image.component.scss @@ -7,14 +7,6 @@ background: var(--background); } -.backdrop { - width: 100vw; - height: 100vh; - opacity: 0.97; - position: absolute; - transition: background-color 300ms; -} - .thumbnail { object-position: center; object-fit: cover; @@ -25,20 +17,6 @@ border-radius: var(--border-radius); } -.hidden { - opacity: 0; -} - -.viewer-image { - object-position: center; - object-fit: cover; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - height: auto; - max-height: 100%; - width: auto; - max-width: 100%; - border-radius: var(--border-radius); +fiv-ripple { + --fiv-color-ripple: rgba(0, 0, 0, 0.5); } diff --git a/projects/core/src/lib/gallery/gallery-image/gallery-image.component.ts b/projects/core/src/lib/gallery/gallery-image/gallery-image.component.ts index 263d73e9..38cad978 100644 --- a/projects/core/src/lib/gallery/gallery-image/gallery-image.component.ts +++ b/projects/core/src/lib/gallery/gallery-image/gallery-image.component.ts @@ -8,264 +8,25 @@ import { EventEmitter } from '@angular/core'; import { SafeResourceUrl } from '@angular/platform-browser'; -import { - trigger, - transition, - style, - animate, - state, - AnimationEvent, - keyframes -} from '@angular/animations'; -import { FivOverlay } from '../../overlay/overlay.component'; @Component({ selector: 'fiv-gallery-image', templateUrl: './gallery-image.component.html', - styleUrls: ['./gallery-image.component.scss'], - animations: [ - trigger('image', [ - transition( - '* => in', - [ - style({ - position: 'absolute', - top: '{{top}}px', - left: '{{left}}px', - transform: 'translate(0%,0%)', - height: '{{height}}px', - width: '{{width}}px', - opacity: 1, - borderRadius: '*' - }), - animate( - '{{timing}}', - style({ - position: 'absolute', - top: '50%', - left: '50%', - transform: 'translate(-50%,-50%)', - height: '*', - width: '*', - opacity: 1, - borderRadius: '0' - }) - ) - ], - { - params: { - top: '0', - left: '0', - height: '*', - width: '*', - timing: '300ms' - } - } - ), - transition( - '* => out', - [ - style({ - position: 'absolute', - top: '{{translate}}px', - left: '50%', - transform: 'translate(-50%,-50%)', - height: '*', - width: '*', - opacity: 1, - borderRadius: '0' - }), - animate( - '{{timing}}', - style({ - position: 'absolute', - top: '{{top}}px', - left: '{{left}}px', - transform: 'translate(0%,0%)', - height: '{{height}}px', - width: '{{width}}px', - opacity: 1, - borderRadius: '*' - }) - ) - ], - { - params: { - top: '0', - left: '0', - height: '*', - width: '*', - translate: '0', - timing: '340ms' - } - } - ), - state('hidden', style({ opacity: 0 })), - transition( - '* => slideout', - [ - style({ - position: 'absolute', - top: '{{translate}}px', - left: '50%', - transform: 'translate(-50%,-50%)', - opacity: 1, - borderRadius: '0' - }), - animate( - '{{timing}}', - style({ - transform: 'translate(-50%,100%)', - opacity: 0.2, - borderRadius: '0' - }) - ) - ], - { - params: { - top: '0', - left: '0', - height: '*', - width: '*', - translate: '0', - timing: '340ms' - } - } - ) - ]), - trigger('fade', [ - transition('void => *', [ - style({ opacity: 0 }), - animate( - '180ms', - keyframes([ - style({ opacity: 0, offset: 0 }), - style({ opacity: 0.3, offset: 0.75 }), - style({ opacity: 0.97, offset: 1 }) - ]) - ) - ]), - transition(':leave', [ - style({ opacity: 0.97 }), - animate( - '180ms', - keyframes([ - style({ opacity: 0.97, offset: 0 }), - style({ opacity: 0.6, offset: 0.8 }), - style({ opacity: 0, offset: 1 }) - ]) - ) - ]) - ]) - ] + styleUrls: ['./gallery-image.component.scss'] }) export class FivGalleryImage implements OnInit { @Input() src: string | SafeResourceUrl; originalSrc: string | SafeResourceUrl; index: number; - @ViewChild('thumbnail') image: ElementRef; - @ViewChild('overlay') overlay: FivOverlay; - - viewerState = 'in'; - animationParams: AnimationParams; - - backdropColor = 'rgb(0,0,0)'; - openTiming = '300ms'; - closeTiming = '340ms'; - + @ViewChild('thumbnail') thumbnail: ElementRef; @Output() willOpen = new EventEmitter(); - @Output() didOpen = new EventEmitter(); - @Output() didClose = new EventEmitter(); constructor() {} ngOnInit() {} open() { - console.log('open', this.src, this.image); this.willOpen.emit(this); - const p = this.getThumbnailPosition(this.image); - this.animationParams = { - translate: p.translate, - timing: this.openTiming, - height: p.height, - width: p.width, - top: p.top, - left: p.left - }; - - console.log('open animation params', this.animationParams); - - this.overlay.show(49999); } - - close(position: Position) { - const p = this.getThumbnailPosition(this.image); - this.animationParams = { - translate: position.translate, - timing: this.closeTiming, - height: p.height, - width: p.width, - top: p.top, - left: p.left - }; - - console.log('animation params', this.animationParams); - this.viewerState = 'out'; - } - - slideOut(position: Position, src) { - this.originalSrc = src; - const p = this.getThumbnailPosition(this.image); - this.animationParams = { - translate: position.translate, - timing: this.closeTiming, - height: p.height, - width: p.width, - top: p.top, - left: p.left - }; - this.viewerState = 'slideout'; - } - - handleViewerAnimation(event: AnimationEvent) { - if (event.fromState === 'void' && event.toState === 'in') { - this.didOpen.emit(this); - } - if ( - (event.fromState === 'in' || event.fromState === 'hidden') && - (event.toState === 'out' || event.toState === 'slideout') - ) { - this.overlay.hide(); - this.didClose.emit(this); - this.viewerState = 'in'; - } - if (event.toState === 'slideout') { - this.src = this.originalSrc; - } - } - - private getThumbnailPosition(element: ElementRef): Position { - const bounds = element.nativeElement.getBoundingClientRect(); - console.log('bounds', bounds, element); - return { - top: bounds.top, - left: bounds.left, - height: element.nativeElement.clientHeight, - width: element.nativeElement.clientWidth - }; - } -} - -export class Position { - top: number; - left: number; - height: number; - width: number; - translate?: number; -} - -export interface AnimationParams extends Position { - timing: string; } diff --git a/projects/core/src/lib/gallery/gallery.component.html b/projects/core/src/lib/gallery/gallery.component.html index 139d637a..fc0c1a9e 100644 --- a/projects/core/src/lib/gallery/gallery.component.html +++ b/projects/core/src/lib/gallery/gallery.component.html @@ -1,45 +1,53 @@ +
- +
+ - - - - - - - - -
- - -
- -
-
-
-
+ + + + + + + - - - - - - - +
+ + +
+ +
+
+
+
- + + + + + \ No newline at end of file diff --git a/projects/core/src/lib/gallery/gallery.component.scss b/projects/core/src/lib/gallery/gallery.component.scss index 962bcb19..19fbb958 100644 --- a/projects/core/src/lib/gallery/gallery.component.scss +++ b/projects/core/src/lib/gallery/gallery.component.scss @@ -1,4 +1,3 @@ - .viewer { width: 100vw; height: 100vh; @@ -46,3 +45,17 @@ ion-slides.in, ion-slides.out { visibility: hidden; } + +.morph { + position: absolute; + object-position: center; + object-fit: cover; + display: block; +} + +.backdrop { + height: 100vh; + width: 100vw; + position: absolute; + background: #000000ee; +} diff --git a/projects/core/src/lib/gallery/gallery.component.ts b/projects/core/src/lib/gallery/gallery.component.ts index 0e47c3d5..da0a5685 100644 --- a/projects/core/src/lib/gallery/gallery.component.ts +++ b/projects/core/src/lib/gallery/gallery.component.ts @@ -1,5 +1,4 @@ import { FivGalleryToolbar } from './gallery-toolbar/gallery-toolbar.component'; -import { ImageService } from './image.service'; import { IonSlides, DomController, Platform } from '@ionic/angular'; import { FivOverlay } from './../overlay/overlay.component'; import { @@ -19,24 +18,32 @@ import { Input, Output, EventEmitter, - OnDestroy + OnDestroy, + ViewChildren } from '@angular/core'; import { style, animate, AnimationBuilder, trigger, - transition + transition, + useAnimation } from '@angular/animations'; import { Key } from './keycodes.enum'; import { DOCUMENT } from '@angular/common'; import { Navigateable } from '../interfaces'; +import { FivGalleryImage } from './gallery-image/gallery-image.component'; +import { from, Subject, zip, of } from 'rxjs'; +import { mergeMap, takeUntil, tap } from 'rxjs/operators'; import { - FivGalleryImage, - Position -} from './gallery-image/gallery-image.component'; -import { from, Subject } from 'rxjs'; -import { mergeMap, takeUntil } from 'rxjs/operators'; + tween, + fromTo, + getPosition, + setPosition, + RectPosition +} from '../animations/tween'; +import { easeOutSine } from '../animations/easing-functions'; +import { fade } from '../animations/animations'; @Component({ selector: 'fiv-gallery', @@ -72,15 +79,40 @@ import { mergeMap, takeUntil } from 'rxjs/operators'; style({ opacity: 1, transform: 'translateY(-100%)' }), animate('75ms', style({ opacity: 0, transform: 'translateY(0%)' })) ]) + ]), + trigger('fade', [ + transition( + '* => void', + useAnimation(fade, { + params: { + fromOpacity: 1, + toOpacity: 0, + time: '240ms' + } + }) + ), + transition( + 'void => *', + useAnimation(fade, { + params: { + fromOpacity: 0, + toOpacity: 1, + time: '240ms' + } + }) + ) ]) ] }) export class FivGallery implements OnInit, AfterContentInit, OnDestroy, Navigateable { @ViewChild('overlay') overlay: FivOverlay; + @ViewChild('morphOverlay') morphOverlay: FivOverlay; @ViewChild('viewer') viewer: ElementRef; + @ViewChild('morph') morphImage: ElementRef; @ViewChild('slider', { read: ElementRef }) swiper: ElementRef; @ViewChild('slider') slides: IonSlides; + @ViewChildren('slideImage') slideImages: QueryList; @ContentChildren(forwardRef(() => FivGalleryImage), { descendants: true }) images: QueryList; @@ -99,7 +131,9 @@ export class FivGallery inFullscreen: boolean; zoomedIn: boolean; controlsVisible = true; - private slidesLoaded; + slidesLoaded = false; + visible = false; + backdrop = false; @Input() pagerVisible = true; @Input() ambient = true; @Input() openTiming = '300ms'; @@ -114,12 +148,7 @@ export class FivGallery @HostListener('window:keyup', ['$event']) keyEvent(event: KeyboardEvent) { - if ( - this.overlay && - this.overlay.open && - this.initialImage && - this.initialImage.viewerState === 'hidden' - ) { + if (this.overlay && this.overlay.open && this.initialImage) { this.handleKeyboardEvents(event); } } @@ -130,8 +159,7 @@ export class FivGallery private animation: AnimationBuilder, private change: ChangeDetectorRef, private platform: Platform, - @Inject(DOCUMENT) private document: any, - private imageService: ImageService + @Inject(DOCUMENT) private document: any ) {} ngOnInit() {} @@ -147,25 +175,15 @@ export class FivGallery } subscribeToImageEvents() { - from(this.images.map(image => image.didOpen)) - .pipe( - mergeMap((value: EventEmitter) => value), - takeUntil(this.$onDestroy) - ) - .subscribe(image => this.open(image)); from(this.images.map(image => image.willOpen)) .pipe( mergeMap((value: EventEmitter) => value), takeUntil(this.$onDestroy) ) - .subscribe(image => this.willOpen.emit(image)); - - from(this.images.map(image => image.didClose)) - .pipe( - mergeMap((value: EventEmitter) => value), - takeUntil(this.$onDestroy) - ) - .subscribe(image => this.didClose.emit(image)); + .subscribe(image => { + this.willOpen.emit(image); + this.open(image); + }); } updateImages() { @@ -199,88 +217,138 @@ export class FivGallery } next() { - if ( - this.overlay && - this.overlay.open && - this.initialImage && - this.initialImage.viewerState === 'hidden' - ) { + if (this.overlay && this.overlay.open && this.initialImage) { this.slides.slideNext(); } } prev() { - if ( - this.overlay && - this.overlay.open && - this.initialImage && - this.initialImage.viewerState === 'hidden' - ) { + if (this.overlay && this.overlay.open && this.initialImage) { this.slides.slidePrev(); } } open(initial: FivGalleryImage) { - console.log('open gallery', initial, initial.src); this.activeIndex = initial.index; this.options.initialSlide = this.activeIndex; + this.visible = false; this.overlay.show(50000); this.initialImage = initial; - this.initialImage.openTiming = this.openTiming; - this.initialImage.closeTiming = this.closeTiming; - this.initialImage.backdropColor = this.ambient - ? this.imageService.getAverageRGB( - this.images.toArray()[this.activeIndex].image.nativeElement - ) - : '#000'; - this.showControls(); + this.initialImage.originalSrc = initial.src; + setTimeout(() => { + //wait a little for ripple + this.backdrop = true; + this.showControls(); + this.morphIn(); + }, 500); } - close() { - this.closeFromPullDown(0); + morphIn() { + this.morphOverlay.show(49999); + const f = getPosition(this.initialImage.thumbnail); + const t = this.calculateImagePosition(); + const tweenDone = new Subject(); + tween(easeOutSine, 320) + .pipe( + fromTo(this.morphImage, 'top', f.top, t.top), + fromTo(this.morphImage, 'left', f.left, t.left), + fromTo(this.morphImage, 'height', f.height, t.height), + fromTo(this.morphImage, 'width', f.width, t.width) + ) + .subscribe({ + complete: () => { + tweenDone.next(); + } + }); + + zip(tweenDone, !this.slidesLoaded ? this.slides.ionSlidesDidLoad : of(true)) + .pipe( + tap(() => { + console.log('VISIBLE'); + this.visible = true; + this.morphOverlay.hide(); + this.didOpen.emit(this.initialImage); + this.swiper.nativeElement.swiper.on('click', () => { + this.handleSingleTap(); + }); + }) + ) + .subscribe(); } + dismiss() { - this.closeFromPullDown(0, false); + this.close(false); } - closeFromPullDown(progress: number, emit = true) { + close(emit = true) { if (emit) { this.willClose.emit(this.initialImage); } - this.transformSlides(0); - + this.backdrop = false; const sameAsInitial = this.images.toArray()[this.activeIndex].index === this.initialImage.index; - const position = this.getImagePosition( - this.images.toArray()[this.activeIndex].image, - progress - ); if (sameAsInitial) { - this.initialImage.close(position); + this.morphBack(); } else { - const src = this.initialImage.src; - this.initialImage.src = this.images.toArray()[this.activeIndex].src; - this.initialImage.slideOut(position, src); + this.slideOut(); } + this.resetPan(0); if (this.inFullscreen) { this.closeFullscreen(); } this.slidesLoaded = false; + } + + morphBack() { + const f = getPosition(this.getActiveImage()); + const t = getPosition(this.initialImage.thumbnail); + this.overlay.hide(); + this.morphOverlay.show(); + tween(easeOutSine, 240) + .pipe( + fromTo(this.morphImage, 'top', f.top, t.top), + fromTo(this.morphImage, 'left', f.left, t.left), + fromTo(this.morphImage, 'height', f.height, t.height), + fromTo(this.morphImage, 'width', f.width, t.width) + ) + .subscribe({ + complete: () => { + this.morphOverlay.hide(); + this.didClose.emit(this.initialImage); + } + }); + } + + slideOut() { + const currentImage = this.getActiveImage(); this.overlay.hide(); + this.morphOverlay.show(); + setPosition(this.morphImage, getPosition(currentImage)); + this.morphImage.nativeElement.src = this.getActiveImage().nativeElement.src; + tween(easeOutSine, 240) + .pipe( + fromTo( + this.morphImage, + 'transform', + 0, + 100, + (t: number) => `translateY(${t}%)` + ) + ) + .subscribe({ + complete: () => { + this.morphImage.nativeElement.style.transform = ''; + this.morphOverlay.hide(); + this.didClose.emit(this.initialImage); + } + }); } - resetPan(progress: number) { - this.resetSlides(progress); + getActiveImage() { + return this.slideImages.toArray()[this.activeIndex]; } - private getImagePosition(element: ElementRef, offset: number = 0): Position { - const bounds = element.nativeElement.getBoundingClientRect(); - return { - top: bounds.top, - left: bounds.left, - height: element.nativeElement.clientHeight, - width: element.nativeElement.clientWidth, - translate: this.platform.height() / 2 + offset * 120 - }; + resetPan(progress: number) { + this.resetSlides(progress); } transformSlides(progress: number) { @@ -326,23 +394,32 @@ export class FivGallery } } - updateBackdrop(index: number) { - this.initialImage.backdropColor = this.ambient - ? this.imageService.getAverageRGB( - this.images.toArray()[index].image.nativeElement - ) - : '#000'; - this.backdropChange.emit(this.initialImage); - } + updateBackdrop(index: number) {} onSlidesLoad() { this.slidesLoaded = true; - this.didOpen.emit(this.initialImage); - this.activeIndex = this.swiper.nativeElement.swiper.activeIndex; - this.initialImage.viewerState = 'hidden'; - this.swiper.nativeElement.swiper.on('click', () => { - this.handleSingleTap(); - }); + } + calculateImagePosition(): RectPosition { + const nH = this.initialImage.thumbnail.nativeElement.naturalHeight; + const nW = this.initialImage.thumbnail.nativeElement.naturalWidth; + let height = Math.min(nH, this.platform.height()); + let width = Math.min(nW, this.platform.width()); + const ratio = nW / nH; + if (ratio * height < width) { + width = height * ratio; + } else { + height = width / ratio; + } + + const top = this.platform.height() / 2 - height / 2; + const left = this.platform.width() / 2 - width / 2; + const p = { + height: height, + width: width, + left: left, + top: top + }; + return p; } fullscreen() { @@ -420,3 +497,11 @@ export class FivGallery this.change.detectChanges(); } } + +export class Position { + top: number; + left: number; + height: number; + width: number; + translate?: number; +} diff --git a/projects/core/src/lib/gallery/gallery.module.ts b/projects/core/src/lib/gallery/gallery.module.ts index c971279e..67beb9c4 100644 --- a/projects/core/src/lib/gallery/gallery.module.ts +++ b/projects/core/src/lib/gallery/gallery.module.ts @@ -1,3 +1,4 @@ +import { FivRippleModule } from './../ripple/ripple.module'; import { FivIconModule } from './../icon/icon.module'; import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; @@ -23,7 +24,8 @@ import { FivGalleryToolbarContent } from './gallery-toolbar-content/gallery-tool FivIconModule, FivIfModule, FivPullModule, - FivOverlayModule + FivOverlayModule, + FivRippleModule ], exports: [ FivGallery, diff --git a/projects/core/src/lib/lazy-image/lazy-image.directive.ts b/projects/core/src/lib/lazy-image/lazy-image.directive.ts index 6d231c1b..b3103e87 100644 --- a/projects/core/src/lib/lazy-image/lazy-image.directive.ts +++ b/projects/core/src/lib/lazy-image/lazy-image.directive.ts @@ -50,7 +50,7 @@ export class FivLazyImage implements OnInit { }); io['POLL_INTERVAL'] = 100; if (this.fivImage) { - io.observe(this.fivImage.image.nativeElement); + io.observe(this.fivImage.thumbnail.nativeElement); } else { io.observe(this.image.nativeElement); } diff --git a/projects/core/src/lib/ripple/ripple.component.scss b/projects/core/src/lib/ripple/ripple.component.scss index 5560f6bd..6a52e84d 100644 --- a/projects/core/src/lib/ripple/ripple.component.scss +++ b/projects/core/src/lib/ripple/ripple.component.scss @@ -1,6 +1,7 @@ :host { display: block; position: relative; + --fiv-color-ripple: rgba(0, 0, 0, 0.1); } :host:hover { @@ -8,7 +9,7 @@ } :host.hover:hover { - background-color: rgba(0, 0, 0, 0.1); + background-color: var(--fiv-color-ripple, rgba(0, 0, 0, 0.1)); } :host.round { @@ -41,7 +42,7 @@ .ripple { position: absolute; - background: rgba(0, 0, 0, 0.1); + background: var(--fiv-color-ripple, rgba(0, 0, 0, 0.1)); border-radius: 100%; transform: scale(0); } diff --git a/projects/core/src/public_api.ts b/projects/core/src/public_api.ts index 1831b220..04a8a066 100644 --- a/projects/core/src/public_api.ts +++ b/projects/core/src/public_api.ts @@ -90,3 +90,5 @@ export * from './lib/back-button/routing-state.service'; export * from './lib/bottom-sheet/drawer-state'; export * from './lib/animations/tween'; +export * from './lib/animations/easing-functions'; +export * from './lib/animations/animations'; diff --git a/src/app/test/test.page.ts b/src/app/test/test.page.ts index b60cc160..73779a30 100644 --- a/src/app/test/test.page.ts +++ b/src/app/test/test.page.ts @@ -1,5 +1,5 @@ import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; -import { tween, fromTo, morph } from '@fivethree/core'; +import { tween, fromTo, morph, linear } from '@fivethree/core'; import { IonContent } from '@ionic/angular'; import { FivOverlay } from '@fivethree/core'; @@ -45,7 +45,7 @@ export class TestPage implements OnInit { const left = Math.random() * (this.content.nativeElement.clientWidth - width); - tween(this.linear, 200) + tween(linear, 200) .pipe( fromTo(this.morphElement, 'left', startPosition.left, left), fromTo(this.morphElement, 'top', startPosition.top, top), @@ -59,7 +59,7 @@ export class TestPage implements OnInit { morph() { this.overlay.show(); - tween(this.linear, 200) + tween(linear, 200) .pipe(morph(this.element, this.morphElement)) .subscribe({ complete: () => console.log('morph complete') }); } @@ -73,55 +73,4 @@ export class TestPage implements OnInit { this.element.nativeElement.style.height = height + 'px'; } } - - linear(t, from, to, duration) { - const c = to - from; - return (c * t) / duration + from; - } - - easeInQuad(t, from, to, duration) { - const c = to - from; - return c * (t /= duration) * t + from; - } - - easeOutSine(t, from, to, duration) { - const c = to - from; - return c * Math.sin((t / duration) * (Math.PI / 2)) + from; - } - - easeInOutQuad(t, from, to, duration) { - const c = to - from; - if ((t /= duration / 2) < 1) { - return (c / 2) * t * t + from; - } else { - return (-c / 2) * (--t * (t - 2) - 1) + from; - } - } - - easeOutElastic(t, b, _c, d) { - let c = _c - b; - let a, p, s; - s = 1.20158; - p = 0; - a = c; - if (t === 0) { - return b; - } else if ((t /= d) === 1) { - return b + c; - } - if (!p) { - p = d * 0.54; - } - if (a < Math.abs(c)) { - a = c; - s = p / 4; - } else { - s = (p / (2 * Math.PI)) * Math.asin(c / a); - } - return ( - a * Math.pow(2, -10 * t) * Math.sin(((t * d - s) * (2 * Math.PI)) / p) + - c + - b - ); - } }