From 64cf3ceed141d659cec0f0f8a0cc45c0c8d816ef Mon Sep 17 00:00:00 2001 From: "Fausto A. Guerrero" Date: Tue, 31 Oct 2017 21:07:48 -0400 Subject: [PATCH 1/2] Progress bar stays loading forever (until completed is called) and animation is much smother --- README.md | 10 ++-- src/slim-loading-bar.component.ts | 9 ++-- src/slim-loading-bar.service.ts | 60 +++++++++++++++++------- tests/slim-loading-bar.component.spec.ts | 11 ++++- tests/slim-loading-bar.service.spec.ts | 58 ++++++++++++++++++++++- 5 files changed, 119 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index c9eaaaf..dbe0ece 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ export class SharedModule { #### 3. Use the `SlimLoadingBarService` for your application - Import `SlimLoadingBarService` from `ng2-slim-loading-bar` in your application code: -```js +```typescript import {Component} from '@angular/core'; import {SlimLoadingBarService} from 'ng2-slim-loading-bar'; @@ -98,9 +98,7 @@ export class AppComponent { constructor(private slimLoadingBarService: SlimLoadingBarService) { } startLoading() { - this.slimLoadingBarService.start(() => { - console.log('Loading complete'); - }); + this.slimLoadingBarService.start(); } stopLoading() { @@ -118,6 +116,7 @@ You can use the following properties to customize the `ng2-slim-loading-bar` com - `color` - The color of loading bar. Default is `firebrick`. It can be any CSS compatible value. - `height` - The height of loading bar. Default value is `2px`. - `show` - The flag helps hide and show the loading bar. Default value is `true`. +- `transitionTimingFunction` - The transition timing function the bar will use when incrementing the progress in the bar. Default value is `'linear'`. Example: `` @@ -127,6 +126,7 @@ You can use the following properties to customize the SlimLoadingBar via instanc - `color` - The color of loading bar. - `height` - The height of loading bar. - `visible` - The flag helps hide and show the loading bar, false for hidden and true for visible. +- `growSpeed` - The speed of the progress bar when start is called. It can be any number grater than 0, but. It is 0.1 by default. You can use the following methods to control the SlimLoadingBar via instance of SlimLoadingBarService: - `start` - Start the loading progress. Use the callback function as an parameter to listed the complete event. @@ -142,7 +142,7 @@ You can hook up with our different types of events thrown. - `SlimLoadingBarEventType.VISIBLE` you can subscribe to these events types by simplying doing this -```js +```typescript constructor(private _loadingBar: SlimLoadingBarService) { this._loadingBar.events.subscribe((item:SlimLoadingBarEvent) => console.log(item)); } diff --git a/src/slim-loading-bar.component.ts b/src/slim-loading-bar.component.ts index 2015e9e..76acc49 100644 --- a/src/slim-loading-bar.component.ts +++ b/src/slim-loading-bar.component.ts @@ -4,7 +4,7 @@ import { Component, Input, OnInit, AfterViewInit, ChangeDetectorRef, ChangeDetectionStrategy, ElementRef } from '@angular/core'; -import { SlimLoadingBarService, SlimLoadingBarEvent, SlimLoadingBarEventType } from './slim-loading-bar.service'; +import { SlimLoadingBarService, SlimLoadingBarEvent, SlimLoadingBarEventType, TransitionTimingFunction } from './slim-loading-bar.service'; import { isPresent } from './slim-loading-bar.utils'; /** @@ -25,7 +25,7 @@ export class SlimLoadingBarComponent implements OnInit, AfterViewInit { private _progress: string = '0'; @Input() set progress(progress: string) { - this.isTransition = progress >= this._progress ? 'all 0.5s ease-in-out' : 'none'; + this.isTransition = progress >= this._progress ? `all ${this.service.interval}ms ${this.transitionTimingFunction}` : 'none'; this._progress = progress; } @@ -36,6 +36,7 @@ export class SlimLoadingBarComponent implements OnInit, AfterViewInit { @Input() color: string = 'firebrick'; @Input() height: string = '2px'; @Input() show: boolean = true; + @Input() transitionTimingFunction: TransitionTimingFunction = 'linear'; constructor(public service: SlimLoadingBarService, private _elmRef: ElementRef, private _changeDetectorRef: ChangeDetectorRef) { } @@ -49,6 +50,8 @@ export class SlimLoadingBarComponent implements OnInit, AfterViewInit { this.height = event.value; } else if (event.type === SlimLoadingBarEventType.VISIBLE) { this.show = event.value; + } else if (event.type === SlimLoadingBarEventType.TRANSITION_TIMING_FUNCTION) { + this.transitionTimingFunction = event.value; } }); } @@ -58,5 +61,5 @@ export class SlimLoadingBarComponent implements OnInit, AfterViewInit { this._elmRef.nativeElement.visible = event.type === SlimLoadingBarEventType.VISIBLE ? event.value : true; this._changeDetectorRef.detectChanges(); }); - } + } } diff --git a/src/slim-loading-bar.service.ts b/src/slim-loading-bar.service.ts index 853955d..b70d0e2 100644 --- a/src/slim-loading-bar.service.ts +++ b/src/slim-loading-bar.service.ts @@ -12,9 +12,12 @@ export enum SlimLoadingBarEventType { PROGRESS, HEIGHT, COLOR, - VISIBLE + VISIBLE, + TRANSITION_TIMING_FUNCTION } +export type TransitionTimingFunction = 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | string; + export class SlimLoadingBarEvent { constructor(public type:SlimLoadingBarEventType, public value:any) {} } @@ -25,13 +28,17 @@ export class SlimLoadingBarEvent { @Injectable() export class SlimLoadingBarService { - private _progress:number = 0; - private _height:string = '2px'; - private _color:string = 'firebrick'; - private _visible:boolean = true; + private _progress: number = 0; + private _height: string = '2px'; + private _color: string = 'firebrick'; + private _visible: boolean = true; + private _transitionTimingFunction: TransitionTimingFunction = 'linear'; + + private _intervalCounterId: any = 0; + public interval: number = 500; // in milliseconds - private _intervalCounterId:any = 0; - public interval:number = 500; // in milliseconds + private growMagnitude: number = 0; + public growSpeed: number = 1 / 10; private eventSource: Subject = new Subject(); public events: Observable = this.eventSource.asObservable(); @@ -86,6 +93,17 @@ export class SlimLoadingBarService { return this._visible; } + set transitionTimingFunction(value: TransitionTimingFunction) { + if (isPresent(value)) { + this._transitionTimingFunction = value; + this.emitEvent(new SlimLoadingBarEvent(SlimLoadingBarEventType.TRANSITION_TIMING_FUNCTION, this._transitionTimingFunction)); + } + } + + get transitionTimingFunction(): TransitionTimingFunction { + return this._transitionTimingFunction; + } + private emitEvent(event: SlimLoadingBarEvent) { if (this.eventSource) { // Push up a new event @@ -94,20 +112,13 @@ export class SlimLoadingBarService { } - start(onCompleted:Function = null) { + start() { // Stop current timer this.stop(); // Make it visible for sure this.visible = true; - // Run the timer with milliseconds iterval - this._intervalCounterId = setInterval(() => { - // Increment the progress and update view component - this.progress++; - // If the progress is 100% - call complete - if (this.progress === 100) { - this.complete(onCompleted); - } - }, this.interval); + // Run the timer with milliseconds interval + this._intervalCounterId = setInterval(this.growProgress.bind(this), this.interval); } stop() { @@ -124,6 +135,7 @@ export class SlimLoadingBarService { complete(onCompleted: Function = null) { this.progress = 100; + this.growMagnitude = 0; this.stop(); setTimeout(() => { // Hide it away @@ -138,6 +150,20 @@ export class SlimLoadingBarService { }, 250); } + protected growProgress() { + this.growMagnitude += 1; + // for all n >= 1 + // progress = sum ((n / (n + 1))^k ) / n , k=1 to m) * 100% + // let m = Infinity + // progress = 100% + // Math .... + this.progress += this.getIncrement(this.growMagnitude) * 100; + } + protected getIncrement(growMagnitude: number) { + const growRateInverse = 1 / this.growSpeed; + // (n / (n + 1))^k ) / n, where k controls how big is the increment ans ensures the infinite sum produces 1 as result + return ((growRateInverse / (growRateInverse + 1)) ** growMagnitude) / growRateInverse; + } } diff --git a/tests/slim-loading-bar.component.spec.ts b/tests/slim-loading-bar.component.spec.ts index 734c01b..300db33 100644 --- a/tests/slim-loading-bar.component.spec.ts +++ b/tests/slim-loading-bar.component.spec.ts @@ -1,9 +1,9 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; -import {SlimLoadingBarService} +import {SlimLoadingBarService} from '../src/slim-loading-bar.service'; -import {SlimLoadingBarComponent} +import {SlimLoadingBarComponent} from '../src/slim-loading-bar.component'; describe('SlimLoadingBar', () => { @@ -78,4 +78,11 @@ describe('SlimLoadingBar', () => { componentFixture.detectChanges(); expect(progressDiv.style.opacity).toBe('1'); }); + + it ('should change the transition timing function when calling set transitionTimingFunction', () => { + component.progress = '30'; + component.transitionTimingFunction = 'ease-in-out'; + componentFixture.detectChanges(); + expect(progressDiv.style.transition).toContain('ease-in-out'); + }); }); diff --git a/tests/slim-loading-bar.service.spec.ts b/tests/slim-loading-bar.service.spec.ts index 4fee295..5646685 100644 --- a/tests/slim-loading-bar.service.spec.ts +++ b/tests/slim-loading-bar.service.spec.ts @@ -33,13 +33,59 @@ describe('SlimLoadingBarService', () => { expect(service.progress).toBe(30); }); - it('increaments over time after calling start()', fakeAsync((): void => { + it('increments over time after calling start()', fakeAsync((): void => { // var value, flag; expect(service.progress).toBe(0); service.start(); tick(500); - expect(service.progress).toBe(1); + expect(service.progress).toBeGreaterThan(0); + service.stop(); + })); + + it('is not completed never after calling start() if no other action is taken', fakeAsync((): void => { + // var value, flag; + expect(service.progress).toBe(0); + service.start(); + + tick(10 * 60 * 1000); // 10 minutes + expect(service.progress).toBeLessThan(100); + service.stop(); + })); + + it('grows in an infinite sum fashion (0.5, 0.75, 0.875, ...) after calling start().', fakeAsync((): void => { + // var value, flag; + expect(service.progress).toBe(0); + service.growSpeed = 1 / 10; + service.start(); + + tick(500); + expect(service.progress).toBeCloseTo(100 * (1 / 11), 1 / 1000); + + tick(500); + expect(service.progress).toBeCloseTo(100 * (21 / 121), 1 / 1000); + + tick(38 * 500); + expect(service.progress).toBeCloseTo(97.7905, 1 / 1000); + + service.stop(); + })); + + it('grow rate after calling calling start() is based in growSpeed.', fakeAsync((): void => { + // var value, flag; + expect(service.progress).toBe(0); + service.growSpeed = 1; + service.start(); + + tick(500); + expect(service.progress).toBeCloseTo(100 * (1 / 2), 1 / 1000); + + tick(500); + expect(service.progress).toBeCloseTo(100 * (3 / 4), 1 / 1000); + + tick(8 * 500); + expect(service.progress).toBeCloseTo(100 * (1023 / 1024), 1 / 1000); + service.stop(); })); @@ -78,4 +124,12 @@ describe('SlimLoadingBarService', () => { expect(service.color).toBe('green'); }); + it('return current transition timing function ', () => { + expect(service.transitionTimingFunction).toBe('linear'); + }); + it('set the transition timing function', () => { + service.transitionTimingFunction = 'ease-in-out'; + expect(service.transitionTimingFunction).toBe('ease-in-out'); + }); + }); From 6fcc9d28570eb6f0e8e46c6589800a750195b69c Mon Sep 17 00:00:00 2001 From: "Fausto A. Guerrero" Date: Tue, 31 Oct 2017 21:38:29 -0400 Subject: [PATCH 2/2] All dev dependencies match --- README.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dbe0ece..35ef241 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ You can hook up with our different types of events thrown. - `SlimLoadingBarEventType.COLOR` - `SlimLoadingBarEventType.VISIBLE` -you can subscribe to these events types by simplying doing this +you can subscribe to these events types simply by doing this ```typescript constructor(private _loadingBar: SlimLoadingBarService) { this._loadingBar.events.subscribe((item:SlimLoadingBarEvent) => console.log(item)); diff --git a/package.json b/package.json index 586acb8..66585af 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "@angular/core": "^2.4.7 || ^4.3.0" }, "devDependencies": { - "@angular/common": "^2.4.7 || ^4.3.0", + "@angular/common": "^2.4.7", "@angular/compiler": "^2.4.7", "@angular/compiler-cli": "^2.4.7", "@angular/core": "^2.4.7",