Skip to content

Commit

Permalink
perf(ScrollCollapse): Use fromEvent observable instead of HostBinding…
Browse files Browse the repository at this point in the history
… for scroll and resize events

Prevent angular's change detection triggering on every pixel scrolled or resized

BREAKING CHANGE: @thisissoon/angular-inviewport module has been added as a peer dependency as
directive now uses WindowRef service
  • Loading branch information
edoparearyee committed Feb 19, 2018
1 parent 0c568cd commit 1348ee5
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 98 deletions.
32 changes: 30 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,58 @@

This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.5.4.

A simple lightweight library for [Angular][angular] with no other dependencies that detects scroll direction and adds a `sn-scrolling-up` or `sn-scrolling-down` class to the element. The library can also detect when the user has scrolled passed the element and apply a `sn-affix` class. Useful for make a element sticky when the user has scrolled beyond it. This library can will also apply `sn-minimise` class after the user has scrolled beyond the height of the element.
A simple lightweight library for [Angular][angular] that detects scroll direction and adds a `sn-scrolling-up` or `sn-scrolling-down` class to the element. The library can also detect when the user has scrolled passed the element and apply a `sn-affix` class. Useful for make a element sticky when the user has scrolled beyond it. This library can will also apply `sn-minimise` class after the user has scrolled beyond the height of the element.

This is a simple library for [Angular][angular], implemented in the [Angular Package Format v5.0](https://docs.google.com/document/d/1CZC2rcpxffTDfRDs6p1cfbmKNLA6x5O-NtkJglDaBVs/edit#heading=h.k0mh3o8u5hx).


## Install

`npm i @thisissoon/angular-scroll-collapse --save`
### via NPM

`npm i @thisissoon/{angular-scroll-collapse,angular-inviewport} --save`

### via Yarn

`yarn add @thisissoon/angular-scroll-collapse @thisissoon/angular-inviewport`

`app.module.ts`
```ts
import { InViewportModule, WindowRef } from '@thisissoon/angular-inviewport';
import { ScrollCollapseModule } from '@thisissoon/angular-scroll-collapse';

@NgModule({
imports: [
InViewportModule.forRoot([
{ provide: WindowRef, useFactory: () => window }
]),
ScrollCollapseModule
]
})
export class AppModule { }
```

`yarn add @thisissoon/angular-scroll-collapse @thisissoon/angular-inviewport`

`app.server.module.ts`
```ts
import { InViewportModule } from '@thisissoon/angular-inviewport';
import { ScrollCollapseModule } from '@thisissoon/angular-scroll-collapse';

@NgModule({
imports: [
InViewportModule.forRoot(), // No need to provide WindowRef for server module
ScrollCollapseModule
]
})
export class AppServerModule { }
```


## Examples

A working example can be found inside [/src](https://github.com/thisissoon/angular-scroll-collapse/tree/master/src) folder.

### Scroll direction

```html
Expand Down
2 changes: 1 addition & 1 deletion index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ export { ScrollCollapseModule } from './src/app/scroll-collapse/scroll-collapse.
export { ScrollCollapseDirective } from './src/app/scroll-collapse/scroll-collapse.directive';
export { affixClass, directionDownClass, directionUpClass, minimiseClass } from './src/app/scroll-collapse/shared/classes';
export { Direction } from './src/app/scroll-collapse/shared/direction.enum';
export { eventLoad, eventPathLoadScroll, eventPathResize, eventResize, eventScroll } from './src/app/scroll-collapse/shared/event-data';
export { eventResize, eventScroll } from './src/app/scroll-collapse/shared/event-data';

43 changes: 43 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@thisissoon/angular-scroll-collapse",
"version": "1.2.1",
"private": false,
"description": "A simple lightweight library for Angular with no other dependencies that detects scroll direction and adds a 'sn-scrolling-up' or 'sn-scrolling-down' class to the element. The library can also detect when the user has scrolled passed the element and apply a 'sn-affix' class. Useful for make a element sticky when the user has scrolled beyond it. This library can will also apply 'sn-minimise' class after the user has scrolled beyond the height of the element",
"description": "A simple lightweight library for Angular that detects scroll direction and adds a 'sn-scrolling-up' or 'sn-scrolling-down' class to the element. The library can also detect when the user has scrolled passed the element and apply a 'sn-affix' class. Useful for make a element sticky when the user has scrolled beyond it. This library can will also apply 'sn-minimise' class after the user has scrolled beyond the height of the element",
"keywords": [
"angular",
"angular 5",
Expand Down Expand Up @@ -44,6 +44,7 @@
},
"peerDependencies": {
"@angular/core": ">=5.0.0 <6.0.0 || >=5.0.0-beta <6.0.0",
"@thisissoon/angular-inviewport": ">=2.1.1 <3.0.0",
"rxjs": ">=5.0.0 <6.0.0"
},
"devDependencies": {
Expand All @@ -59,12 +60,14 @@
"@angular/platform-browser": "5.2.5",
"@angular/platform-browser-dynamic": "5.2.5",
"@angular/router": "5.2.5",
"@thisissoon/angular-inviewport": "^2.1.1",
"@types/jasmine": "~2.8.3",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "^4.1.0",
"core-js": "^2.4.1",
"coveralls": "^3.0.0",
"cz-conventional-changelog": "^2.1.0",
"jasmine-core": "~2.8.0",
"jasmine-spec-reporter": "~4.1.0",
"karma": "~2.0.0",
Expand All @@ -81,5 +84,10 @@
"tslint": "~5.7.0",
"typescript": "2.6.2",
"zone.js": "^0.8.20"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
}
}
10 changes: 6 additions & 4 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { InViewportModule, WindowRef } from '@thisissoon/angular-inviewport';

import { ScrollCollapseModule } from './scroll-collapse';
import { AppComponent } from './app.component';


@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
InViewportModule.forRoot([
{ provide: WindowRef, useFactory: () => window }
]),
ScrollCollapseModule
],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule { }
55 changes: 40 additions & 15 deletions src/app/scroll-collapse/scroll-collapse.directive.spec.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,65 @@
import { fakeAsync, tick } from '@angular/core/testing';
import { ElementRef, SimpleChanges } from '@angular/core';
import { WindowRef } from '@thisissoon/angular-inviewport';

import { ScrollCollapseDirective } from './scroll-collapse.directive';

describe('ScrollCollapseDirective', () => {
let node: HTMLElement;
let el: ElementRef;
let directive: ScrollCollapseDirective;
let ngZone;
let windowRef;

beforeEach(() => {
node = document.createElement('p');
el = new ElementRef(node);
directive = new ScrollCollapseDirective(el);
directive.ngAfterContentInit();
ngZone = {
run: jasmine.createSpy('run').and.callFake((fn) => fn()),
runOutsideAngular: jasmine.createSpy('runOutsideAngular').and.callFake((fn) => fn())
};
windowRef = {
triggerEvent: null,
scrollX: 0,
scrollY: 0,
innerWidth: 1366,
innerHeight: 768,
addEventListener: (name, fn) => windowRef.triggerEvent = fn,
removeEventListener: () => null
};
directive = new ScrollCollapseDirective(el, ngZone, windowRef);
directive.ngAfterViewInit();
});

describe('scroll event', () => {
it('should emit event value', fakeAsync(() => {
const spy = spyOn(directive.viewport$, 'next').and.callThrough();
directive.eventHandler(768, 1366, 0, 0);
tick(100);
directive.eventHandler(768, 1366, 200, 0);
it('should call event handler', fakeAsync(() => {
const spy = spyOn(directive, 'onScrollOrResizeEvent').and.callThrough();
const events = [
{ scrollX: 0, scrollY: 0, width: 1366, height: 768 },
{ scrollX: 0, scrollY: 50, width: 1366, height: 768 }
];
directive.ngAfterViewInit();
windowRef.triggerEvent();
tick(50);
expect(spy).not.toHaveBeenCalled();
windowRef.scrollY = 50;
windowRef.triggerEvent();
tick(100);
expect(spy).toHaveBeenCalledWith({
width: 1366,
height: 768,
scrollY: 200,
scrollX: 0
});
expect(spy).toHaveBeenCalledWith(events);
expect(directive.isScrollingUp).toBeFalsy();
expect(directive.isScrollingDown).toBeTruthy();

windowRef.scrollY = 0;
windowRef.triggerEvent();
tick(100);
expect(spy.calls.mostRecent().args).toEqual([events.reverse()]);
expect(directive.isScrollingUp).toBeTruthy();
expect(directive.isScrollingDown).toBeFalsy();
}));

it('should remove event handler on destroy', () => {
const spy = spyOn(directive, 'calculateScrollDirection');
const spy = spyOn(directive, 'onScrollOrResizeEvent').and.callThrough();
directive.ngOnDestroy();
directive.eventHandler(768, 1366, 200, 0);
expect(spy).not.toHaveBeenCalled();
});
});
Expand Down
Loading

0 comments on commit 1348ee5

Please sign in to comment.