Skip to content

Commit

Permalink
Merge pull request #11 from maxisam/feat/continue-loading
Browse files Browse the repository at this point in the history
feat: continue loading
  • Loading branch information
maxisam committed Feb 11, 2020
2 parents 9a930eb + 3887e51 commit 8adc8a5
Show file tree
Hide file tree
Showing 9 changed files with 2,461 additions and 1,710 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Support Angular >=6.0.0

- Load image directly for spider (SEO friendly) or non-supported browsers

- Aggressive Loading. Able to continue to load even if images haven't got intersected when the concurrently loading count is lower than a certain value. (_after 4.0.0_)

## Install

```bat
Expand Down Expand Up @@ -137,7 +139,7 @@ For `ngx-image-placeholder` component, it takes

- placeholderImageSrc

(after 3.0.0, you can set imageRatio and placeholderImageSrc directly on `ngxProgressiveImage` and spare ngx-image-placeholder layer)
(_after 3.0.0_, you can set imageRatio and placeholderImageSrc directly on `ngxProgressiveImage` and spare ngx-image-placeholder layer)

For `ngx-progressive-image-loader` component, it takes

Expand All @@ -147,6 +149,10 @@ For `ngx-progressive-image-loader` component, it takes

- filter

- isAggressiveLoading: boolean; default to `true`; Set to `true` to enable **Aggressive Loading** feature. (_after 4.0.0_)

- concurrentLoading: number; default t0 4; Decided at least how many concurrent loading when **Aggressive Loading** is enabled

For `ngxProgressiveImage` directive, (only for image or source elements)

- imageRatio
Expand Down
21 changes: 12 additions & 9 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": 1,
"newProjectRoot": "projects",
"projects": {
"package-host": {
"library-host": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
Expand All @@ -13,7 +13,7 @@
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/package-host",
"outputPath": "dist/library-host",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
Expand Down Expand Up @@ -45,19 +45,19 @@
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "package-host:build",
"browserTarget": "library-host:build",
"port": 5200
},
"configurations": {
"production": {
"browserTarget": "package-host:build:production"
"browserTarget": "library-host:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "package-host:build"
"browserTarget": "library-host:build"
}
},
"test": {
Expand All @@ -81,19 +81,19 @@
}
}
},
"package-host-e2e": {
"library-host-e2e": {
"root": "e2e/",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "package-host:serve"
"devServerTarget": "library-host:serve"
},
"configurations": {
"production": {
"devServerTarget": "package-host:serve:production"
"devServerTarget": "library-host:serve:production"
}
}
},
Expand Down Expand Up @@ -140,7 +140,10 @@
}
}
},
"defaultProject": "package-host",
"defaultProject": "library-host",
"cli": {
"packageManager": "yarn"
},
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
Expand Down
40 changes: 21 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"name": "package-host",
"name": "ngx-progressive-image-loader",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"prettier": "prettier --write \"**/*.{js,json,css,md,ts,html,component.html}\"",
"prettier": "prettier --write \"**/*.{json,md,ts,html,component.html}\"",
"start": "ng serve",
"build": "npm run build:lib",
"build:lib": "ng build ngx-progressive-image-loader && npm run copy-scss && npm run copy-readme",
"build:lib:w": "ng build ngx-progressive-image-loader --watch",
"publish:lib": "npm publish ./dist/ngx-progressive-image-loader",
"publish:lib:next": "npm publish ./dist/ngx-progressive-image-loader --tag next",
"publish:lib": "yarn publish ./dist/ngx-progressive-image-loader",
"publish:lib:next": "yarn publish ./dist/ngx-progressive-image-loader --tag next",
"copy-scss": "cpx \"./projects/ngx-progressive-image-loader/src/lib/*.scss\" \"./dist/ngx-progressive-image-loader/\" --verbose",
"copy-readme": "cpx \"./README.md\" \"./dist/ngx-progressive-image-loader\"",
"test": "ng test ngx-progressive-image-loader",
Expand All @@ -27,22 +27,25 @@
"@angular/platform-browser-dynamic": "~7.2.0",
"@angular/router": "~7.2.0",
"core-js": "^2.5.4",
"ngx-logger": "^3.3.13",
"ngx-progressive-image-loader": "^4.0.0",
"ngx-window-token": "^2.0.1",
"node-sass": "^4.12.0",
"rxjs": "~6.3.3",
"tslib": "^1.9.0",
"zone.js": "~0.8.26",
"ngx-progressive-image-loader": "^4.0.0-beta.12",
"ngx-window-token": "^2.0.1"
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.13.0",
"@angular-devkit/build-ng-packagr": "~0.13.0",
"@angular/cli": "~7.3.4",
"@angular/compiler-cli": "~7.2.0",
"@angular/language-service": "~7.2.0",
"@types/node": "~8.9.4",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "~4.5.0",
"cpx": "^1.5.0",
"husky": "1.3.1",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
Expand All @@ -60,18 +63,17 @@
"tslib": "^1.9.0",
"tslint": "~5.11.0",
"tslint-config-prettier": "1.18.0",
"typescript": "~3.2.2",
"cpx": "^1.5.0"
"typescript": "~3.2.2"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,json,css,md,ts,html,component.html}": [
"prettier --write",
"git add"
]
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,json,css,md,ts,html,component.html}": [
"prettier --write",
"git add"
]
}
}
6 changes: 3 additions & 3 deletions projects/ngx-progressive-image-loader/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ngx-progressive-image-loader",
"version": "4.0.0-beta.1",
"version": "4.0.0",
"author": {
"name": "Sam Lin",
"email": "maxisam@gmail.com"
Expand All @@ -25,7 +25,7 @@
"ngx-window-token": ">=2.0.0"
},
"peerDependencies": {
"@angular/common": ">=6.0.0 || >=7.0.0 || >=8.0.0",
"@angular/core": ">=6.0.0 || >=7.0. || >=8.0.00"
"@angular/common": ">=6.0.0",
"@angular/core": ">=6.0.0"
}
}
10 changes: 7 additions & 3 deletions projects/ngx-progressive-image-loader/src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { InjectionToken } from '@angular/core';
export interface IImageLoaderOptions extends IntersectionObserverInit {
placeholderImageSrc?: string;
imageRatio: number;
filter: string;
filter?: string;
isAggressiveLoading?: boolean;
concurrentLoading?: number;
}
export const IMAGE_LOADER_CONFIG_TOKEN = new InjectionToken<IImageLoaderOptions>(
'Image loader Config'
Expand All @@ -12,7 +14,9 @@ export const IMAGE_LOADER_CONFIG_TOKEN = new InjectionToken<IImageLoaderOptions>
export const DEFAULT_IMAGE_LOADER_OPTIONS = <IImageLoaderOptions>{
// root?: Element | null;
rootMargin: '10px',
threshold: 0.1,
threshold: [0.1, 0.5, 1],
imageRatio: 16 / 9,
placeholderImageSrc: ''
placeholderImageSrc: '',
isAggressiveLoading: true,
concurrentLoading: 4
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import { ProgressiveImageLoaderComponent } from '../progressive-image-loader/pro
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ImagePlaceholderComponent implements OnInit {
@HostBinding('class')
class = 'ngx-image-placeholder';
@HostBinding('class') class = 'ngx-image-placeholder';
@HostBinding('style')
get placeHolder(): SafeStyle {
return this.sanitizer.bypassSecurityTrustStyle(
Expand All @@ -22,11 +21,9 @@ export class ImagePlaceholderComponent implements OnInit {
}

// to create a placeholder before finish loading the real image to avoid reflow
@Input()
imageRatio: number;
@Input() imageRatio: number;
// a loading image showing before the real image is loaded
@Input()
placeholderImageSrc: string;
@Input() placeholderImageSrc: string;

get imageFilter(): SafeStyle {
return this.sanitizer.bypassSecurityTrustStyle(`${this._ProgressiveImageLoader.filter}`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { isPlatformBrowser } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
ElementRef,
Inject,
Input,
OnDestroy,
OnInit,
Optional,
PLATFORM_ID,
Renderer2
} from '@angular/core';
Expand All @@ -16,32 +17,52 @@ import { isSpider, isSupportIntersectionObserver, loadImage } from '../util';

@Component({
selector: 'ngx-progressive-image-loader',
exportAs: 'ngxProgressiveImageLoader',
template: `
<ng-content></ng-content>
`
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProgressiveImageLoaderComponent implements OnInit, OnDestroy {
// define the placeholder height for all images inside this components
@Input()
imageRatio: number;

@Input()
filter: string;
@Input() imageRatio: number;
// how many image at least should load at the same time
@Input() concurrentLoading: number;
// is going to load images that are being observed but haven't been intersected yet when loading counter < concurrentLoading
@Input() isAggressiveLoading: boolean;
@Input() filter: string;
// the src of loading image
@Input()
placeholderImageSrc: string;
@Input() placeholderImageSrc: string;

intersectionObserver: IntersectionObserver;
// to store observed images
targetMap = new Map();
// to maintain the sequence of observed images
targetQueue = <string[]>[];
// counter of current loading images
loading = 0;
public get isObservable(): boolean {
return !!this.intersectionObserver;
}

constructor(
element: ElementRef,
public _Renderer: Renderer2,
public _ConfigurationService: ConfigurationService,
@Inject(PLATFORM_ID) private platformId: any,
@Inject(WINDOW) private window: any
@Optional()
@Inject(WINDOW)
private window: any
) {}

ngOnInit() {
if (this.isAggressiveLoading === undefined) {
this.isAggressiveLoading = this._ConfigurationService.config.isAggressiveLoading;
}
if (this.concurrentLoading === undefined) {
this.concurrentLoading = this._ConfigurationService.config.concurrentLoading;
}
if (
this.window &&
isSupportIntersectionObserver(this.window) &&
!isSpider(this.window) &&
isPlatformBrowser(this.platformId)
Expand All @@ -63,21 +84,54 @@ export class ProgressiveImageLoaderComponent implements OnInit, OnDestroy {
}
}

onIntersectionChanged(entries: IntersectionObserverEntry[], observer: IntersectionObserver) {
entries.forEach(
entry =>
entry.isIntersecting &&
this.onImageAppearsInViewport(entry.target as HTMLImageElement, observer)
);
observe(target: HTMLImageElement) {
// so intersection observer can always detect it correctly, otherwise image elements with 0 in height sometime don't load correctly
target.style.minHeight = '1rem';
this.intersectionObserver.observe(target);
this.targetMap.set(target.dataset.src, target);
this.targetQueue.push(target.dataset.src);
}

onImageAppearsInViewport(image: HTMLImageElement, observer: IntersectionObserver) {
unobserve(target: HTMLImageElement) {
target.style.minHeight = 'initial';
this.targetMap.delete(target.dataset.src);
this.intersectionObserver.unobserve(target);
}
// called after an image loaded
imageLoaded() {
this.loading--;
while (
this.isAggressiveLoading &&
this.targetQueue &&
this.targetQueue.length &&
this.loading <= this.concurrentLoading
) {
const next = this.targetQueue.shift();
this.targetMap.has(next) && this.loadImage(this.targetMap.get(next));
}
}
onIntersectionChanged(entries: IntersectionObserverEntry[], observer: IntersectionObserver) {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.loadImage(entry.target as HTMLImageElement);
}
});
}
// start loading an image
loadImage(image: HTMLImageElement) {
// Stop observing the current target
observer.unobserve(image);
this.unobserve(image);
this.loading++;
loadImage(this._Renderer, image);
}

reset() {
this.targetQueue = [];
this.targetMap = new Map();
this.isObservable && this.intersectionObserver.disconnect();
}
ngOnDestroy(): void {
this.intersectionObserver && this.intersectionObserver.disconnect();
this.isObservable && this.intersectionObserver.disconnect();
this.intersectionObserver = this.targetQueue = this.targetMap = undefined;
}
}
Loading

0 comments on commit 8adc8a5

Please sign in to comment.