Skip to content

Commit

Permalink
馃帹
Browse files Browse the repository at this point in the history
closing #270
  • Loading branch information
tjoskar committed Jun 27, 2019
1 parent a45a338 commit 2374cbf
Show file tree
Hide file tree
Showing 18 changed files with 151 additions and 175 deletions.
2 changes: 1 addition & 1 deletion example/src/app/pages/onload.component.ts
Expand Up @@ -31,7 +31,7 @@ export class OnLoadComponent {

constructor(private cd: ChangeDetectorRef) {}

onLoadImage(success) {
onLoadImage(success: boolean) {
if (success) {
this.isLoading = false;
this.cd.detectChanges();
Expand Down
2 changes: 1 addition & 1 deletion example/src/app/pages/scroll-container.component.ts
Expand Up @@ -49,7 +49,7 @@ import { Component, ChangeDetectionStrategy, ElementRef } from '@angular/core';
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ScrollContainerComponent {
myScrollContainer;
myScrollContainer!: HTMLElement;
images = [
'https://images.unsplash.com/photo-1467932760935-519284fc87fa?dpr=2&auto=compress,format&fit=crop&w=1199&h=800&q=80',
'https://images.unsplash.com/photo-1468103933896-2c34a78104c2?dpr=2&auto=compress,format&fit=crop&w=1199&h=799&q=80',
Expand Down
30 changes: 15 additions & 15 deletions src/hooks-factory.ts
Expand Up @@ -2,19 +2,19 @@ import { scrollPreset } from './scroll-preset';
import { HookSet, ModuleOptions } from './types';

export function cretateHooks<E>(options?: ModuleOptions<E>): HookSet<any> {
if (!options) {
return scrollPreset;
}
const hooks = {};
if (options.preset) {
Object.assign(hooks, options.preset);
} else {
Object.assign(hooks, scrollPreset);
}
Object.keys(options)
.filter(key => key !== 'preset')
.forEach(key => {
hooks[key] = options[key];
});
return hooks as HookSet<any>;
if (!options) {
return scrollPreset;
}
const hooks = {};
if (options.preset) {
Object.assign(hooks, options.preset);
} else {
Object.assign(hooks, scrollPreset);
}
Object.keys(options)
.filter(key => key !== 'preset')
.forEach(key => {
hooks[key] = options[key];
});
return hooks as HookSet<any>;
}
2 changes: 1 addition & 1 deletion src/intersection-observer-preset/index.ts
@@ -1 +1 @@
export { intersectionObserverPreset } from './preset'
export { intersectionObserverPreset } from './preset';
6 changes: 3 additions & 3 deletions src/intersection-observer-preset/intersection-listener.ts
Expand Up @@ -16,7 +16,7 @@ function loadingCallback(entrys: IntersectionObserverEntry[]) {
entrys.forEach(entry => intersectionSubject.next(entry));
}

export const getIntersectionObserver = (attributes: Attributes) => {
export const getIntersectionObserver = (attributes: Attributes): Observable<IntersectionObserverEntry> => {
if (!attributes.scrollContainer && !isWindowDefined()) {
return empty();
}
Expand All @@ -39,11 +39,11 @@ export const getIntersectionObserver = (attributes: Attributes) => {

observer.observe(attributes.element);

return Observable.create(obs => {
return Observable.create((obs: Subject<IntersectionObserverEntry>) => {
const subscription = intersectionSubject.pipe(filter(entry => entry.target === attributes.element)).subscribe(obs);
return () => {
subscription.unsubscribe();
observer.unobserve(attributes.element);
observer!.unobserve(attributes.element);
};
});
};
52 changes: 21 additions & 31 deletions src/lazyload-image.directive.ts
@@ -1,39 +1,27 @@
import {
AfterContentInit,
Directive,
ElementRef,
EventEmitter,
Inject,
Input,
NgZone,
OnChanges,
OnDestroy,
Optional,
Output
} from '@angular/core';
import { ReplaySubject } from 'rxjs';
import { AfterContentInit, Directive, ElementRef, EventEmitter, Inject, Input, NgZone, OnChanges, OnDestroy, Optional, Output } from '@angular/core';
import { ReplaySubject, Observable, Subscription } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { cretateHooks } from './hooks-factory';
import { lazyLoadImage } from './lazyload-image';
import { Attributes, HookSet, ModuleOptions } from './types';
import { isWindowDefined } from './util';

@Directive({
selector: '[lazyLoad]'
selector: '[lazyLoad]'
})
export class LazyLoadImageDirective implements OnChanges, AfterContentInit, OnDestroy {
@Input('lazyLoad') lazyImage; // The image to be lazy loaded
@Input() defaultImage: string; // The image to be displayed before lazyImage is loaded
@Input() errorImage: string; // The image to be displayed if lazyImage load fails
@Input() scrollTarget: any; // Scroll container that contains the image and emits scoll events
@Input() scrollObservable; // Pass your own scroll emitter
@Input() offset: number; // The number of px a image should be loaded before it is in view port
@Input() useSrcset: boolean; // Whether srcset attribute should be used instead of src
@Input('lazyLoad') lazyImage!: string; // The image to be lazy loaded
@Input() defaultImage?: string; // The image to be displayed before lazyImage is loaded
@Input() errorImage?: string; // The image to be displayed if lazyImage load fails
@Input() scrollTarget?: any; // Scroll container that contains the image and emits scoll events
@Input() scrollObservable?: Observable<any>; // Pass your own scroll emitter
@Input() offset?: number; // The number of px a image should be loaded before it is in view port
@Input() useSrcset?: boolean; // Whether srcset attribute should be used instead of src
@Output() onLoad: EventEmitter<boolean> = new EventEmitter(); // Callback when an image is loaded
private propertyChanges$: ReplaySubject<Attributes>;
private elementRef: ElementRef;
private ngZone: NgZone;
private scrollSubscription;
private scrollSubscription?: Subscription;
private hooks: HookSet<any>;

constructor(el: ElementRef, ngZone: NgZone, @Optional() @Inject('options') options?: ModuleOptions) {
Expand All @@ -50,7 +38,7 @@ export class LazyLoadImageDirective implements OnChanges, AfterContentInit, OnDe
defaultImagePath: this.defaultImage,
errorImagePath: this.errorImage,
useSrcset: this.useSrcset,
offset: this.offset | 0,
offset: this.offset ? this.offset | 0 : 0,
scrollContainer: this.scrollTarget,
scrollObservable: this.scrollObservable
});
Expand All @@ -63,16 +51,18 @@ export class LazyLoadImageDirective implements OnChanges, AfterContentInit, OnDe
}

this.ngZone.runOutsideAngular(() => {
this.scrollSubscription = this.propertyChanges$.pipe(
tap(attributes => this.hooks.setup(attributes)),
switchMap(attributes => this.hooks.getObservable(attributes).pipe(lazyLoadImage(this.hooks, attributes)))
).subscribe(success => this.onLoad.emit(success));
this.scrollSubscription = this.propertyChanges$
.pipe(
tap(attributes => this.hooks.setup(attributes)),
switchMap(attributes => this.hooks.getObservable(attributes).pipe(lazyLoadImage(this.hooks, attributes)))
)
.subscribe(success => this.onLoad.emit(success));
});
}

ngOnDestroy() {
[this.scrollSubscription]
.filter(subscription => subscription && !subscription.isUnsubscribed)
.forEach(subscription => subscription.unsubscribe());
if (this.scrollSubscription) {
this.scrollSubscription.unsubscribe();
}
}
}
44 changes: 22 additions & 22 deletions src/scroll-preset/__test__/preset.test.ts
Expand Up @@ -18,7 +18,7 @@ describe('isVisible', () => {

it('Should be vissible when top is inside viewport and no offset', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(999, 100),
offset: 0
};
Expand All @@ -30,7 +30,7 @@ describe('isVisible', () => {

it('Should not be vissible when the image is outside viewport', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(1001, 100),
offset: 0
};
Expand All @@ -42,7 +42,7 @@ describe('isVisible', () => {

it('Should be vissible when the image is outside viewport but have a offset', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(-399, 100),
offset: 100
};
Expand All @@ -54,7 +54,7 @@ describe('isVisible', () => {

it('Should not be vissible when the image is inside viewport but have a offset', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(901, 100),
offset: -100
};
Expand All @@ -65,7 +65,7 @@ describe('isVisible', () => {

it('Should be vissible when the image is inside viewport and have a offset', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(899, 100),
offset: -100
};
Expand All @@ -77,7 +77,7 @@ describe('isVisible', () => {

it('Should not be vissible when the bottom of the image is inside viewport but have a offset', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(-201, 100),
offset: -100
};
Expand All @@ -89,7 +89,7 @@ describe('isVisible', () => {

it('Should be vissible when the bottom of the image is inside viewport and have a offset', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(-199, 100),
offset: -100
};
Expand All @@ -100,7 +100,7 @@ describe('isVisible', () => {

it('Should be vissible when the image is larger than the viewport', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(-100, -100, 1200, 1200),
offset: 0
};
Expand All @@ -111,7 +111,7 @@ describe('isVisible', () => {

it('Should not be vissible when the image is to the left of the viewport', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(100, -301),
offset: 0
};
Expand All @@ -122,7 +122,7 @@ describe('isVisible', () => {

it('Should not be vissible when the image is to the right of the viewport', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(100, 1001),
offset: 0
};
Expand All @@ -133,7 +133,7 @@ describe('isVisible', () => {

it('Should be vissible when the left side is in viewport', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(200, 899),
offset: -100
};
Expand All @@ -144,7 +144,7 @@ describe('isVisible', () => {

it('Should be vissible when the right side is in viewport', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(200, -199),
offset: -100
};
Expand All @@ -155,7 +155,7 @@ describe('isVisible', () => {

it('Should be vissible when only left side with no corners is in the viewport', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(-100, 500, 1200, 1200),
offset: 0
};
Expand All @@ -166,7 +166,7 @@ describe('isVisible', () => {

it('Should be vissible when only top side with no corners is in the viewport', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(500, -100, 1200, 1200),
offset: 0
};
Expand All @@ -177,7 +177,7 @@ describe('isVisible', () => {

it('Should be vissible when only right side with no corners is in the viewport', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(-100, -500, 1200, 1200),
offset: 0
};
Expand All @@ -188,7 +188,7 @@ describe('isVisible', () => {

it('Should be vissible when only bottom side with no corners is in the viewport', () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(-500, -100, 1200, 1200),
offset: 0
};
Expand All @@ -199,7 +199,7 @@ describe('isVisible', () => {

it("Should not be visible when image is horizontally in window's view, but not in scroll-container's", () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(800, 0, 1200, 1200),
offset: 0,
scrollContainer: generateElement(0, 0, 700, 1200)
Expand All @@ -211,7 +211,7 @@ describe('isVisible', () => {

it("Should not be visible when image is vertically in window's view, but not in scroll-container's", () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(0, 800, 1200, 1200),
offset: 0,
scrollContainer: generateElement(0, 0, 1200, 700)
Expand All @@ -223,7 +223,7 @@ describe('isVisible', () => {

it("Should not be visible when image is not in window's view, but is in scroll-container's", () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(1400, 0, 1200, 1200),
offset: 0,
scrollContainer: generateElement(1300, 0, 1200, 1200)
Expand All @@ -235,7 +235,7 @@ describe('isVisible', () => {

it("Should be visible when image is in window's and scroll-container's view", () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(100, 0, 1200, 1200),
offset: 0,
scrollContainer: generateElement(0, 0, 700, 1200)
Expand All @@ -247,7 +247,7 @@ describe('isVisible', () => {

it("Should not be visible when image's rect is empty", () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(0, 0, 0, 0),
offset: 0
};
Expand All @@ -258,7 +258,7 @@ describe('isVisible', () => {

it("Should not be visible when image's rect is empty and has an offset", () => {
const attribute = {
event: undefined,
event: undefined as any,
element: generateElement(0, 0, 0, 0),
offset: 100
};
Expand Down

0 comments on commit 2374cbf

Please sign in to comment.