1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Angular] 画面を引っ張ってリロード (pull to refresh)

Last updated at Posted at 2019-10-22

これはなに?

画面を下に引っ張って(pan down)してリロードする機能が欲しくて作りました。

pull-to-refresh-sample.gif

このサンプルは、実際にはリロードせず、リロード処理が必要になった回数をカウントして表示しているだけです。

同じようなものはすでにいくつかあると思います。
もしかすると、すでにAngular Materialに入っているかもしれません。
薄目で見てやってください。

元ソース

こちら https://stackblitz.com/edit/angular-rxjs-pull-to-refresh を元にしています。

元ソースから以下の点を変更しています。

  • アイコン(丸→矢印、ドロップシャドウ付加)
  • リロード中にアイコン回転 → 引っ張っている最中にアイコン回転
  • リロードが終わるとアイコンが消える → ただちにアイコンが消える
  • RxJS 6対応

ソースコード

こちらに置きました。

主要ファイル

主なファイルです。

src
└ app
  ├ pull-to-refresh
  │ └ pull-to-refresh.component.ts  タッチイベントを受け取りアイコンを表示するコンポーネント
  └ services 
    └ load-notify.service.ts 他コンポーネント等へリロードすることを通知するためのサービス

ちょっと解説

自分でもよくわからなくなりそうなRxJS周りの処理を解説します。

捕まえるイベント

下記は、タッチイベントを拾うObservableです。

src/app/pull-to-refresh/pull-to-refresh.component.ts
  private readonly touchstart$ = fromEvent<TouchEvent>(document, 'touchstart');
  private readonly touchend$ = fromEvent<TouchEvent>(document, 'touchend');
  private readonly touchmove$ = fromEvent<TouchEvent>(document, 'touchmove');

アイコンの場所と回転の制御

下記は、アイコンの移動と回転をさせるためのパラメータを流すObservableです。

touchstartイベントが流れたら、代わりにtouchmoveイベントを購読し、移動量を流すようにしています。
touchendイベントが流れるまで購読し終わったら、現在位置から画面トップまでマイナス方向の値を流します。

src/app/pull-to-refresh/pull-to-refresh.component.ts
  private drag$ = this.touchstart$.pipe(
    switchMap(start => {
      let pos = TOP_POSITION;

      return concat(
        this.touchmove$.pipe(
          map(move => move.touches[0].pageY - start.touches[0].pageY),
          tap(p => (pos = p)),
          filter(p => p < this.pullDistance),
          takeUntil(this.touchend$)
        ),
        defer(() => this.tweenObservable(pos, TOP_POSITION, 200)) // 位置を戻す
      );
    }),
    repeat()
  );

リロードするように通知

下記は、コンポーネント初期化時のコールバックです。
サービスを介して画面のリロードが必要になったことを通知します。

touchstartイベントが流れたら、代わりにtouchendイベントを購読し、移動量を流しています。
移動量が規定値以上であれば画面リロードを通知します。

src/app/pull-to-refresh/pull-to-refresh.component.ts
  ngOnInit(): void {
    // 指を離した時に、規定距離を移動していたらリフレッシュ
    this.touchstart$
      .pipe(
        switchMap(start => {
          return this.touchend$.pipe(
            map(x => x.changedTouches[0].pageY - start.touches[0].pageY)
          );
        }),
        filter(p => p >= this.pullDistance)
      )
      .subscribe(() => this.loadNotifyService.notify());
  }

感想

  • HammerJSとAngular組み込みのアニメーション機能を使って実装していましたが、どうにも期待する動きになりませんでした。
  • イベントを絡めたコンポーネントのユニットテストは難しいです(すみません。今回書いてません)。
  • RxJSたのしぃ〜。

最後までお読みいただきありがとうございました。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?