2017/07/11

Vue.js2.x系でInfinite Scroll(無限スクロール)を実装する


Infinite Scroll(無限スクロール)とは、TwitterやfacebookをはじめとしたSNSやモバイルアプリなどでよく使われている、スクロールすることで自動的に次のコンテンツを読み込む機能だ。
従来のページネーションでは、1ページに表示できるコンテンツが限られたり、「Prev/Next」などのボタンをクリックすることで以降のコンテンツを読み込んだり、モバイルユーザには不便な点が多い。そのため、モバイルデバイスではボタンによる更新よりも無限スクロールのほうが好んで使われている。


ということで、当記事ではVue.js2.x系(サンプルコードはv2.2.1で動作確認)でInfinite Scrollの実装方法をまとめる。

Infinite Scrollを実装する


<!-- index.html -->
<div id="app">
  <div class="item-container" @scroll="infiniteScroll">
    <div v-for="item in items" class="item-card">
      <div class="thumbnail"></div>
      <h3 class="title">{{ item.title }}</h3>
    </div>
    <div class="loader"><p>Now loading......</p></div>
  </div>
</div>
// script.js
// mock
function api (page, limit) {
  const items = [...Array(limit)].map((a, i) => {
    return {
      title: `page: ${page}, limit: ${limit}, ${i.toString().repeat(10)}`
    };
  });
  
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(items);
    }, 2000);
  });
}

const app = new Vue({
  el: '#app',
  data () {
    return {
      page: 0,
      limit: 10,
      items: []
    }
  },
  created () {
    this.fetch();
  },
  methods: {
    async fetch () {
      const items = await api(this.page, this.limit);
      this.items.push(...items);
      this.page++;
    },
    infiniteScroll (event) {  
      // スクロールの現在位置 + 親(.item-container)の高さ >= スクロール内のコンテンツの高さ
      if ((event.target.scrollTop + event.target.offsetHeight) >= event.target.scrollHeight) {
        this.fetch();
      }
    }
  }
});

Infinite Scrollを実現するためには、スクロールイベントでの処理が必須になるので、無限スクロールしたい要素に@scroll="infiniteScroll"v-on:scroll="infiniteScroll"でもOK)でイベントを登録し、スクロールされる度にinfiniteScrollメソッドが呼ばれるようにする。

infiniteScrollメソッドの引数には対象のイベントが渡ってくるので、eventのtarget(ここではdiv.item-container)を使ってスクロール位置、要素の高さ、要素内のコンテンツの高さを取得し、最後までスクロールされたかどうかを判定している。

最下部に来たらthis.fetchメソッドでコンテンツの読み込みをするという、わりと簡単な構造。

ちなみにスクロール関連のプロパティの意味は以下のとおり。

  • element.scrollTop
    • 要素のコンテンツがスクロールされるピクセル数のプロパティ
    • element.scrollTopでどれだけスクロールしたか取得できる
    • element.scrollTop = 100 のようにすると上部から100ピクセル分スクロールできる
  • element.offsetHeight
    • 要素の高さ(padding, border含む)のプロパティ
    • 読み取り専用プロパティ
  • element.scrollHeight
    • スクロールできる分(画面に表示されていない分)も含めた要素の高さ
    • 読み取り専用プロパティ


scrollTop + offsetHeight >= scrollHeight で最後までスクロールしたかどうか判定できる。詳しくはjsFiddleのサンプルコードにデバッグ情報を表示しているので、そこを見ていただければすぐ理解できると思う。


ここまでInfinite Scrollの実装方法を説明してきたが、実はVue.jsの有名なUIコンポーネント(ElementUI)を作っているチームが、vue-infinite-scroll というカスタムディレクティブを公開している。最近更新されてなかったり、Issueが溜まっていたりと若干不安だが、当記事の実装をするのが面倒なときには使ってみてください。



参考サイト






以上

written by @bc_rikko

0 件のコメント :

コメントを投稿