<!DOCTYPE html>
<html>
<head>
<!-- Load common libraries -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/typescript/2.1.1/typescript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.4.1/core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/zone.js/0.8.5/zone.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"></script>
<!-- Configure SystemJS -->
<script src="systemjs.config.js"></script>
<script>
System
.import('main.ts')
.catch(console.error.bind(console));
</script>
<style>body { font-family: Roboto, Arial, sans-serif; }</style>
</head>
<body>
<app>Loading ...</app>
</body>
</html>
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {BrowserModule} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {NgModule} from '@angular/core';
import {AppComponent} from './app.component';
import {TurnGreenWhenInViewComponent} from './turn-green-when-in-view.component.ts';
import {HttpModule} from '@angular/http';
import {AppearDirective} from './appear.ts';
@NgModule({
exports: [
]
})
export class PlunkerMaterialModule {}
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule
],
declarations: [AppComponent, TurnGreenWhenInViewComponent, AppearDirective],
bootstrap: [AppComponent],
providers: []
})
export class PlunkerAppModule {}
platformBrowserDynamic().bootstrapModule(PlunkerAppModule);
import {Component} from '@angular/core';
@Component({
selector: 'app',
template: `
<div style="height: 1500px; background: #ccc;">scroll down slowly</div>
<turn-green-when-in-view></turn-green-when-in-view>
<div style="height: 1500px; background: #ccc;"></div>
`
})
export class AppComponent {}
/** Add Transpiler for Typescript */
System.config({
transpiler: 'typescript',
typescriptOptions: {
emitDecoratorMetadata: true
},
packages: {
'.': {
defaultExtension: 'ts'
},
'vendor': {
defaultExtension: 'js'
}
}
});
System.config({
map: {
'main': 'main.js',
// Angular specific mappings.
'@angular/core': 'https://unpkg.com/@angular/core/bundles/core.umd.js',
'@angular/animations': 'https://unpkg.com/@angular/animations/bundles/animations.umd.js',
'@angular/common': 'https://unpkg.com/@angular/common/bundles/common.umd.js',
'@angular/compiler': 'https://unpkg.com/@angular/compiler/bundles/compiler.umd.js',
'@angular/http': 'https://unpkg.com/@angular/http/bundles/http.umd.js',
'@angular/forms': 'https://unpkg.com/@angular/forms/bundles/forms.umd.js',
'@angular/router': 'https://unpkg.com/@angular/router/bundles/router.umd.js',
'@angular/platform-browser': 'https://unpkg.com/@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'https://unpkg.com/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/animations/browser': 'https://unpkg.com/@angular/animations/bundles/animations-browser.umd.js',
'@angular/platform-browser/animations': 'https://unpkg.com/@angular/platform-browser/bundles/platform-browser-animations.umd.js',
'@angular/cdk': 'https://unpkg.com/@angular/cdk/bundles/cdk.umd.js',
// Rxjs mapping
'rxjs': 'https://unpkg.com/rxjs',
},
packages: {
// Thirdparty barrels.
'rxjs': { main: 'index' },
}
});
import {Component} from '@angular/core';
@Component({
selector: 'turn-green-when-in-view',
template: `
<div (appear)="onAppear()"
[ngStyle]="{backgroundColor: hasAppeared ? 'green' : 'red'}" style="height: 500px; line-height: 500px; color: #fff;">
<span [hidden]="hasAppeared">I haven't appeared yet.</span>
<span [hidden]="!hasAppeared">I have appeared.</span>
</div>
`,
styles: [`
:host {
display: block;
}
`]
})
export class TurnGreenWhenInViewComponent {
hasAppeared: boolean;
onAppear(){
this.hasAppeared = true;
}
}
import {
ElementRef, Output, Directive, AfterViewInit, OnDestroy, EventEmitter
} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {Subscription} from 'rxjs/Subscription';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/startWith';
@Directive({
selector: '[appear]'
})
export class AppearDirective implements AfterViewInit, OnDestroy {
@Output()
appear: EventEmitter<void>;
elementPos: number;
elementHeight: number;
scrollPos: number;
windowHeight: number;
subscriptionScroll: Subscription;
subscriptionResize: Subscription;
constructor(private element: ElementRef){
this.appear = new EventEmitter<void>();
}
saveDimensions() {
this.elementPos = this.getOffsetTop(this.element.nativeElement);
this.elementHeight = this.element.nativeElement.offsetHeight;
this.windowHeight = window.innerHeight;
}
saveScrollPos() {
this.scrollPos = window.scrollY;
}
getOffsetTop(element: any){
let offsetTop = element.offsetTop || 0;
if(element.offsetParent){
offsetTop += this.getOffsetTop(element.offsetParent);
}
return offsetTop;
}
checkVisibility(){
if(this.isVisible()){
// double check dimensions (due to async loaded contents, e.g. images)
this.saveDimensions();
if(this.isVisible()){
this.unsubscribe();
this.appear.emit();
}
}
}
isVisible(){
return this.scrollPos >= this.elementPos || (this.scrollPos + this.windowHeight) >= (this.elementPos + this.elementHeight);
}
subscribe(){
this.subscriptionScroll = Observable.fromEvent(window, 'scroll').startWith(null)
.subscribe(() => {
this.saveScrollPos();
this.checkVisibility();
});
this.subscriptionResize = Observable.fromEvent(window, 'resize').startWith(null)
.subscribe(() => {
this.saveDimensions();
this.checkVisibility();
});
}
unsubscribe(){
if(this.subscriptionScroll){
this.subscriptionScroll.unsubscribe();
}
if(this.subscriptionResize){
this.subscriptionResize.unsubscribe();
}
}
ngAfterViewInit(){
this.subscribe();
}
ngOnDestroy(){
this.unsubscribe();
}
}