Angularで下にスクロールすると消えて、上にスクロールすると現れるヘッダー


FacebookのSPアプリとかのあれをAngularで実現します。
jQuery、Angular1.x系ではHeadroom.jsというライブラリで実現できるらしのですが、
残念ながらAngular2/4系には対応していなかったので、別の方法で実装することにしました。

実現したいのはこの動き↓
Headroom.js Demo

参考

jQueryで実装している方がいたので、これを参考にしました。大変参考になりました。
http://weboook.blog22.fc2.com/blog-entry-412.html

Windowイベントを取得するために HostListener を使いました。
http://blog.yuhiisk.com/archive/2016/05/12/angular2-window-event.html

DOMアクセスには DOCUMENT を使いました。
https://angular.io/api/platform-browser/DOCUMENT

やったこと

まずは、ベースとなるAngularプロジェクトを作成します。
今回はangular-cliでベースのプロジェクトを作成しました。

ng new angular-hide-header-sample

ベースにしたプロジェクト

src
├── src/app
│   ├── src/app/app.component.css
│   ├── src/app/app.component.html
│   ├── src/app/app.component.spec.ts
│   ├── src/app/app.component.ts
│   └── src/app/app.module.ts
├── src/assets
├── src/environments
│   ├── src/environments/environment.prod.ts
│   └── src/environments/environment.ts
├── src/favicon.ico
├── src/index.html
├── src/main.ts
├── src/polyfills.ts
├── src/styles.css
├── src/test.ts
├── src/tsconfig.app.json
├── src/tsconfig.spec.json
└── src/typings.d.ts

ヘッダーコンポーネント作成

angular-cliで作成したプロジェクトにヘッダーコンポーネントを追加します。

ng g component header
header.component.html
<nav class="header">
  <!-- 実際にはここにmenuを実装 -->
  <p>header</p>
</nav>
header.component.css
.header {
  position: fixed;
  z-index: 99;
  top: 0;
  left: 0;
  width: 100%;
  height: 60px;
  background: rgba(0,0,0,.5);
  transition: .5s;
}

ヘッダーコンポーネントをapp.componentに追加

angular-cliで作成した際に作成されるtopページにそのまま追記しました。

app.component.html
<app-header></app-header>
<!--The content below is only a placeholder and can be replaced.-->
<!-- ...略 -->

この時点で、npm startを実行しブラウザからlocalhost:4200を確認すると、次のように表示されているはずです。 この時点では、hederは最上部に固定されたままですが、このあと更に修正していきます。

ヘッダーコンポーネントを修正

実際に表示を切り替えるロジックを実装していきます。

ヘッダーの位置を動的に変更するために、[style.top.px]でコンポーネントからcssの値を設定できるようにします。

header.component.html
<nav class="header" [style.top.px]="headerPos">
  <p>header</p>
  <!-- 実際にはここにmenuを実装 -->
</nav>

Inject HostListener DOCUMENTをそれぞれインポート
クラスのメンバー変数のうち、ofsetにはヘッダーの高さを指定します。

header.component.ts
import { Component, OnInit, Inject, HostListener } from '@angular/core';
import { DOCUMENT } from '@angular/platform-browser';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
  private ofset: number = 60;
  private startPos: number = 0;
  private headerPos: number = 0;


  constructor(@Inject(DOCUMENT) private document: Document) { }
  ngOnInit() {
  }

  @HostListener('window:scroll', ['$event'])
  onScroll(event) {
    let currentPos = this.document.body.scrollTop;
    if(currentPos > this.startPos) {
      if(this.document.body.scrollTop >= this.ofset) {
        this.headerPos = -this.ofset;
      }
    } else {
      this.headerPos = 0;
    }
    this.startPos = currentPos;
  }

}

完成

次のような感じで、それっぽく動きます。

サンプル

今回作成した、サンプルプロジェクトはGitHubに置きました。