From f73ef12e4ffbc5fa7e62a3a427579efea450695b Mon Sep 17 00:00:00 2001 From: Murhaf Sousli Date: Mon, 6 Aug 2018 12:49:47 +0300 Subject: [PATCH 1/3] Run gestures outside angular zone --- .../components/gallery-slider.component.ts | 84 ++++++++++--------- .../components/gallery-thumbs.component.ts | 62 +++++++------- 2 files changed, 78 insertions(+), 68 deletions(-) diff --git a/projects/core/src/lib/components/gallery-slider.component.ts b/projects/core/src/lib/components/gallery-slider.component.ts index f182afe3..1b70263f 100644 --- a/projects/core/src/lib/components/gallery-slider.component.ts +++ b/projects/core/src/lib/components/gallery-slider.component.ts @@ -6,16 +6,18 @@ import { OnInit, OnChanges, Inject, + NgZone, ElementRef, EventEmitter, ChangeDetectionStrategy, PLATFORM_ID } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; -import { GalleryState, GalleryConfig, SlidingDirection } from '../models'; - import { BehaviorSubject, Observable, Subscription, fromEvent } from 'rxjs'; import { map, tap, debounceTime } from 'rxjs/operators'; +import { GalleryState, GalleryConfig, SlidingDirection } from '../models'; +import { SliderState, WorkerState } from '../models/slider.model'; +import { Gallery } from '../services/gallery.service'; declare const Hammer: any; @@ -24,11 +26,11 @@ declare const Hammer: any; changeDetection: ChangeDetectionStrategy.OnPush, preserveWhitespaces: false, template: ` -
-
@@ -49,7 +51,7 @@ declare const Hammer: any; export class GallerySliderComponent implements OnInit, OnChanges, OnDestroy { /** Sliding worker */ - private readonly _slidingWorker$ = new BehaviorSubject({value: 0, active: false}); + private readonly _slidingWorker$ = new BehaviorSubject({value: 0, active: false}); /** HammerJS instance */ private _hammer: any; @@ -58,7 +60,7 @@ export class GallerySliderComponent implements OnInit, OnChanges, OnDestroy { private _resizeSub$: Subscription; /** Stream that emits sliding state */ - slider$: Observable<{ style: any, active: boolean }>; + sliderState$: Observable; /** Gallery state */ @Input() state: GalleryState; @@ -80,14 +82,13 @@ export class GallerySliderComponent implements OnInit, OnChanges, OnDestroy { return {transform: `perspective(50px) translate3d(0, 0, ${-this.config.zoomOut}px)`}; } - constructor(private _el: ElementRef, @Inject(PLATFORM_ID) private platform: Object) { + constructor(private gallery: Gallery, private _el: ElementRef, private _zone: NgZone, @Inject(PLATFORM_ID) private platform: Object) { // Activate sliding worker - this.slider$ = this._slidingWorker$.pipe(map( - (state: any) => ({ - style: this.sliderStyle(state.value), - active: state.active - }))); + this.sliderState$ = this._slidingWorker$.pipe(map((state: WorkerState) => ({ + style: this.getSliderState(state), + active: state.active + }))); } ngOnChanges() { @@ -102,24 +103,26 @@ export class GallerySliderComponent implements OnInit, OnChanges, OnDestroy { this._hammer = new Hammer(this._el.nativeElement); this._hammer.get('pan').set({direction: Hammer.DIRECTION_ALL}); - // Move the slider - this._hammer.on('pan', (e) => { - - switch (this.config.slidingDirection) { - case SlidingDirection.Horizontal: - this.updateSlider({value: e.deltaX, active: true}); - if (e.isFinal) { - this.updateSlider({value: 0, active: false}); - this.horizontalPan(e); - } - break; - case SlidingDirection.Vertical: - this.updateSlider({value: e.deltaY, active: true}); - if (e.isFinal) { - this.updateSlider({value: 0, active: false}); - this.verticalPan(e); - } - } + this._zone.runOutsideAngular(() => { + // Move the slider + this._hammer.on('pan', (e) => { + + switch (this.config.slidingDirection) { + case SlidingDirection.Horizontal: + this.updateSlider({value: e.deltaX, active: true}); + if (e.isFinal) { + this.updateSlider({value: 0, active: false}); + this.horizontalPan(e); + } + break; + case SlidingDirection.Vertical: + this.updateSlider({value: e.deltaY, active: true}); + if (e.isFinal) { + this.updateSlider({value: 0, active: false}); + this.verticalPan(e); + } + } + }); }); } @@ -131,10 +134,12 @@ export class GallerySliderComponent implements OnInit, OnChanges, OnDestroy { ).subscribe(); } - // Fix wrong slider width on init + // This component width is set to 100%, therefore its width gives 0 at the beginning + // TODO: find a cleaner way to refresh the gallery once slider width is available + // Current workaround: wait 500ms then refresh the slider to fix gallery items' width setTimeout(() => { this.updateSlider({value: 0, active: false}); - }, 300); + }, 500); } ngOnDestroy() { @@ -147,19 +152,22 @@ export class GallerySliderComponent implements OnInit, OnChanges, OnDestroy { this._slidingWorker$.complete(); } - private sliderStyle(delta: number) { + /** + * Convert sliding state to styles + */ + private getSliderState(state: WorkerState): any { switch (this.config.slidingDirection) { case SlidingDirection.Horizontal: return { - transform: `translate3d(${-(this.state.currIndex * this._el.nativeElement.offsetWidth) + delta}px, 0, 0)`, + transform: `translate3d(${-(this.state.currIndex * this._el.nativeElement.offsetWidth) + state.value}px, 0, 0)`, width: this._el.nativeElement.offsetWidth * this.state.items.length + 'px', height: '100%' }; case SlidingDirection.Vertical: return { - transform: `translate3d(0, ${-(this.state.currIndex * this._el.nativeElement.offsetHeight) + delta}px, 0)`, + transform: `translate3d(0, ${-(this.state.currIndex * this._el.nativeElement.offsetHeight) + state.value}px, 0)`, width: '100%', - height: this._el.nativeElement.offsetHeight * this.state.items.length + 'px', + height: this._el.nativeElement.offsetHeight * this.state.items.length + 'px' }; } } @@ -204,7 +212,7 @@ export class GallerySliderComponent implements OnInit, OnChanges, OnDestroy { this.action.emit('prev'); } - private updateSlider(state: any) { - this._slidingWorker$.next({...this._slidingWorker$, ...state}); + private updateSlider(state: WorkerState) { + this._slidingWorker$.next({...this._slidingWorker$.value, ...state}); } } diff --git a/projects/core/src/lib/components/gallery-thumbs.component.ts b/projects/core/src/lib/components/gallery-thumbs.component.ts index d419fe3b..1e576ecc 100644 --- a/projects/core/src/lib/components/gallery-thumbs.component.ts +++ b/projects/core/src/lib/components/gallery-thumbs.component.ts @@ -6,14 +6,15 @@ import { OnInit, OnChanges, HostBinding, + NgZone, ElementRef, EventEmitter, ChangeDetectionStrategy } from '@angular/core'; -import { GalleryConfig, GalleryState, ThumbnailsPosition, ThumbnailsMode } from '../models'; - import { BehaviorSubject, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { GalleryConfig, GalleryState, ThumbnailsPosition, ThumbnailsMode } from '../models'; +import { SliderState, WorkerState } from '../models/slider.model'; declare const Hammer: any; @@ -22,11 +23,11 @@ declare const Hammer: any; changeDetection: ChangeDetectionStrategy.OnPush, preserveWhitespaces: false, template: ` -
-
+
({value: 0, active: false}); /** HammerJS instance */ private _hammer: any; @@ -52,7 +53,7 @@ export class GalleryThumbsComponent implements OnInit, OnChanges, OnDestroy { private _freeModeCurrentOffset = 0; /** Stream that emits sliding state */ - slider$: Observable<{ style: any, active: boolean }>; + sliderState$: Observable; /** Gallery state */ @Input() state: GalleryState; @@ -72,14 +73,13 @@ export class GalleryThumbsComponent implements OnInit, OnChanges, OnDestroy { /** Host width */ @HostBinding('style.width') width: string; - constructor(private _el: ElementRef) { + constructor(private _el: ElementRef, private _zone: NgZone) { // Activate sliding worker - this.slider$ = this._slidingWorker$.pipe( - map((state: any) => ({ - style: this.thumbsStyle(state.value), - active: state.active - }))); + this.sliderState$ = this._slidingWorker$.pipe(map((state: WorkerState) => ({ + style: this.getSliderState(state), + active: state.active + }))); } ngOnChanges() { @@ -95,14 +95,16 @@ export class GalleryThumbsComponent implements OnInit, OnChanges, OnDestroy { this._hammer = new Hammer(this._el.nativeElement); this._hammer.get('pan').set({direction: Hammer.DIRECTION_ALL}); - // Move the slider - switch (this.config.thumbMode) { - case ThumbnailsMode.Strict: - this._hammer.on('pan', (e) => this.strictMode(e)); - break; - case ThumbnailsMode.Free: - this._hammer.on('pan', (e) => this.freeMode(e)); - } + this._zone.runOutsideAngular(() => { + // Move the slider + switch (this.config.thumbMode) { + case ThumbnailsMode.Strict: + this._hammer.on('pan', (e) => this.strictMode(e)); + break; + case ThumbnailsMode.Free: + this._hammer.on('pan', (e) => this.freeMode(e)); + } + }); } } @@ -187,14 +189,14 @@ export class GalleryThumbsComponent implements OnInit, OnChanges, OnDestroy { /** * Convert sliding state to styles */ - private thumbsStyle(delta: number) { + private getSliderState(state: WorkerState): any { let value: number; switch (this.config.thumbPosition) { case ThumbnailsPosition.Top: case ThumbnailsPosition.Bottom: this.width = '100%'; this.height = this.config.thumbHeight + 'px'; - value = -(this.state.currIndex * this.config.thumbWidth) - (this.config.thumbWidth / 2 - delta); + value = -(this.state.currIndex * this.config.thumbWidth) - (this.config.thumbWidth / 2 - state.value); return { transform: `translate3d(${value}px, 0, 0)`, width: this.state.items.length * this.config.thumbWidth + 'px', @@ -204,16 +206,16 @@ export class GalleryThumbsComponent implements OnInit, OnChanges, OnDestroy { case ThumbnailsPosition.Right: this.width = this.config.thumbWidth + 'px'; this.height = '100%'; - value = -(this.state.currIndex * this.config.thumbHeight) - (this.config.thumbHeight / 2 - delta); + value = -(this.state.currIndex * this.config.thumbHeight) - (this.config.thumbHeight / 2 - state.value); return { transform: `translate3d(0, ${value}px, 0)`, width: '100%', - height: this.state.items.length * this.config.thumbHeight + 'px', + height: this.state.items.length * this.config.thumbHeight + 'px' }; } } - private verticalPan(e) { + private verticalPan(e: any) { if (e.velocityY > 0.3) { this.prev(); } else if (e.velocityY < -0.3) { @@ -229,7 +231,7 @@ export class GalleryThumbsComponent implements OnInit, OnChanges, OnDestroy { } } - private horizontalPan(e) { + private horizontalPan(e: any) { if (e.velocityX > 0.3) { this.prev(); } else if (e.velocityX < -0.3) { @@ -253,7 +255,7 @@ export class GalleryThumbsComponent implements OnInit, OnChanges, OnDestroy { this.action.emit('prev'); } - private updateSlider(state: any) { - this._slidingWorker$.next({...this._slidingWorker$, ...state}); + private updateSlider(state: WorkerState) { + this._slidingWorker$.next({...this._slidingWorker$.value, ...state}); } } From 789f8f58c25434e38c4415f173f60f2203f67294 Mon Sep 17 00:00:00 2001 From: Murhaf Sousli Date: Mon, 6 Aug 2018 12:50:10 +0300 Subject: [PATCH 2/3] Add slider state interfaces --- projects/core/src/lib/models/slider.model.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 projects/core/src/lib/models/slider.model.ts diff --git a/projects/core/src/lib/models/slider.model.ts b/projects/core/src/lib/models/slider.model.ts new file mode 100644 index 00000000..083335f3 --- /dev/null +++ b/projects/core/src/lib/models/slider.model.ts @@ -0,0 +1,9 @@ +export interface SliderState { + style: any; + active: boolean; +} + +export interface WorkerState { + value: number; + active: boolean; +} From ceb6a26a15552c00ee8fbeeb3b9a2f63b71bbd74 Mon Sep 17 00:00:00 2001 From: Murhaf Sousli Date: Mon, 6 Aug 2018 12:50:27 +0300 Subject: [PATCH 3/3] tiny refactor --- projects/core/src/lib/components/gallery.component.ts | 4 ++-- projects/core/src/lib/directives/lazy.directive.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/projects/core/src/lib/components/gallery.component.ts b/projects/core/src/lib/components/gallery.component.ts index 9e4e48a6..5662d288 100644 --- a/projects/core/src/lib/components/gallery.component.ts +++ b/projects/core/src/lib/components/gallery.component.ts @@ -137,10 +137,10 @@ export class GalleryComponent implements OnInit, OnChanges, OnDestroy { // Subscribes to indexChange and itemsChange events when user bind them if (this.indexChange.observers.length) { - this._indexChange$ = this.galleryRef.indexChanged.subscribe((e: GalleryState) => this.indexChange.next(e)); + this._indexChange$ = this.galleryRef.indexChanged.subscribe((state: GalleryState) => this.indexChange.next(state)); } if (this.itemsChange.observers.length) { - this._itemChange$ = this.galleryRef.itemsChanged.subscribe((e: GalleryState) => this.itemsChange.next(e)); + this._itemChange$ = this.galleryRef.itemsChanged.subscribe((state: GalleryState) => this.itemsChange.next(state)); } } diff --git a/projects/core/src/lib/directives/lazy.directive.ts b/projects/core/src/lib/directives/lazy.directive.ts index e82544da..52538147 100644 --- a/projects/core/src/lib/directives/lazy.directive.ts +++ b/projects/core/src/lib/directives/lazy.directive.ts @@ -11,7 +11,7 @@ export class LazyDirective implements OnDestroy { private _worker$ = new Subject(); @Input('lazyImage') - set lazyImage(imagePath) { + set lazyImage(imagePath: string) { this.loadImage(imagePath); } @@ -47,7 +47,7 @@ export class LazyDirective implements OnDestroy { ).subscribe(); } - loadImage(imagePath) { + loadImage(imagePath: string) { this._worker$.next(imagePath); }