Barba.jsを使ってシームレスな画面遷移を実装する

Blog

Barba.jsとは

今回は、シームレスなページ遷移を実現できる「Barba.js」について書きたいと思います。

Barba.jsとは、
XMLHttpRequestによる非同期通信処理を行い、現在閲覧しているページ以外からも情報を取得することが可能なajax
閲覧履歴を操作するpush state API
この二つを利用したpjax
そして、さらにそれに動きを付けてインタラクティブな画面遷移を可能するのがBarba.jsです。

Barba.js公式ページ
http://barbajs.org/

アプリのような動きをしたWebサイトを作ることができて、なおかつUXも高めることができるという優れものなのです!

メリットとデメリット

とても楽しく便利なものなので、どんなサイトも使ったらいいじゃないか!
確かにその通りなのですが、ライブラリにはデメリットは付きもの。しっかりと良し悪しを把握しましょう。

メリット

ページ遷移が早くて楽しい

デメリット

読み込み時のscriptが動かない
実態は最初に閲覧したページのため、相対パスがずれる
meta情報が変わらない

そうです、メリットよりデメリットの方がはるかに多いため、取扱いには注意が必要です。

しかし、しっかりとデメリットを考慮してあげれば素晴らしいサイトを作ることができます!!

導入

まずは公式ページよりダウンロードして、HTMLに読み込みます。

<script src="/js/barba.min.js"></script>

Barba.jsで注意すべき点は、実際にページが遷移する訳ではないということ。
あくまでajaxを利用してページの一部を書き換えるだけであって、実態は最初に読み込みしたページです。
そのためBarba.jsを利用するページの枠部分のブロック構造はすべて共通化できていた方が利用しやすいかと思います。
そしてHTMLには、下記のように指定のIDとclassを付与してください。

<div id="barba-wrapper">
  <div class="barba-container">
    <!-- ここが入れ替わります -->
  </div>
</div>

各ページの.barba-containerとした部分が書き換えられる形になります。
もしIDやclassを変更したい場合は、

Barba.Pjax.Dom.wrapperId = 'wrapper';
Barba.Pjax.Dom.containerClass = 'container';

として、変更することもできます。
このあたりは公式のドキュメントに色々とあるので参考にしてください。

そして、javascriptには、

Barba.Pjax.start();

とすれば、最低限は動くかと思います。

アニメーションをつける

アニメーションをさせるには、Barba.BaseTransition.extend(newObject)を使用します。
promice();を利用するとタイミングも制御できてやりやすくなるかと思います。

/* アニメーションの関数を記述
--------------------------------------------------*/
var PageTransition = Barba.BaseTransition.extend({

  start: function() {
    Promise
      .all([this.newContainerLoading, this.moveOut()])
      .then(this.moveIn.bind(this));
  },

  // 遷移前の処理(内容はお好みで)
  moveOut: function() {
    moveOutAnim();
    return $(this.oldContainer).animate({ opacity: 0 }, 800).promise();
  },

  // 遷移後の処理(内容はお好みで)
  moveIn: function() {
    var _this = this;
    var $el = $(this.newContainer);
    window.scrollTo( 0, 0 );
    $(this.oldContainer).hide();
    $el.css({
      visibility : 'visible',
      opacity : 0
    });
    moveInAnim();
    $el.animate({ opacity: 1 }, 400, function() {
      _this.done();
    });
  }
});

/* アニメーションの関数を実行
--------------------------------------------------*/
Barba.Pjax.getTransition = function() {
  return PageTransition;
};

またページごとに処理を変えることもできます。
まずはHTMLのbarba-container部分にdata-namespaceを付与します。

<div id="barba-wrapper">
  <div class="barba-container" data-namespace="home">
    <!-- ここが入れ替わります -->
  </div>
</div>

そして、namespaceを指定し、それに合わせてBarba.BaseView.extend(newObject)を実行します。

// ページごとの処理を記述
var HomeTransition = Barba.BaseView.extend({
  namespace: 'home',
  onEnter: function() {
    // 読み込みを開始した時の処理
  },
  onEnterCompleted: function() {
    // トランジションを完了した時の処理
  }
  onLeave: function() {
    // 次のページへのトランジションが始まった時の処理
  },
  onLeaveCompleted: function() {
    // このページのcontainerが削除された時の処理
  }
});
HomeTransition.init();

head内を書き換え、Googleアナリティクスに情報を送る

Barba.jsにはいくつかイベントが用意されているため、タイミングに合わせて動作させたい処理を記述します。
例えばSEOを考慮し、headタグ内のmetaタグを書き換えて、Googleアナリティクスに情報を送りたい時は、


/* headタグ内の書き換え --------------------------------------------------*/ Barba.Dispatcher.on('newPageReady', function(currentStatus, oldStatus, container, newPageRawHTML) { var head = document.head; var newPageRawHead = newPageRawHTML.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]; var newPageHead = document.createElement('head'); newPageHead.innerHTML = newPageRawHead; var removeHeadTags = [ "meta[name='description']", "meta[property^='og']", "meta[name^='twitter']", "link[rel='canonical']" ].join(','); var headTags = head.querySelectorAll(removeHeadTags) for(var i = 0; i < headTags.length; i++ ){ head.removeChild(headTags[i]); } var newHeadTags = newPageHead.querySelectorAll(removeHeadTags) for(var i = 0; i < newHeadTags.length; i++ ){ head.appendChild(newHeadTags[i]); } }); /* Googleアナリティクスに情報を送る --------------------------------------------------*/ Barba.Dispatcher.on('initStateChange', function() { if (typeof ga === 'function') { ga('send', 'pageview', window.location.pathname.replace(/^\/?/, '/') + window.location.search); } });

とすれば、ページに合わせてSEOへの対策もできます。

最後にコピペ用にすべてをまとめるとこんな形。
上記には登場しなかったメソッド等もありますが、公式のドキュメントを元に利用してみてください。

Barba.Pjax.init();
Barba.Prefetch.init();

// headタグ内の書き換え
Barba.Dispatcher.on('newPageReady', function(currentStatus, oldStatus, container, newPageRawHTML) {

  var head = document.head;
  var newPageRawHead = newPageRawHTML.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0];
  var newPageHead = document.createElement('head');
  newPageHead.innerHTML = newPageRawHead;

  var removeHeadTags = [
    "meta[name='description']",
    "meta[property^='og']",
    "meta[name^='twitter']",
    "link[rel='canonical']"
  ].join(',');
  var headTags = head.querySelectorAll(removeHeadTags)
  for(var i = 0; i < headTags.length; i++ ){
    head.removeChild(headTags[i]);
  }
  var newHeadTags = newPageHead.querySelectorAll(removeHeadTags)

  for(var i = 0; i < newHeadTags.length; i++ ){
    head.appendChild(newHeadTags[i]);
  }

});

// Googleアナリティクスに情報を送る
Barba.Dispatcher.on('initStateChange', function() {
  if (typeof ga === 'function') {
    ga('send', 'pageview', window.location.pathname.replace(/^\/?/, '/') + window.location.search);
  }
});

// ページごとの処理
var HomeTransition = Barba.BaseView.extend({
  namespace: 'home',
  onEnter: () => {
    // 読み込みを開始した時の処理
  },
  onEnterCompleted: () => {
    // トランジションを完了した時の処理
  }
});
HomeTransition.init();

// 共通アニメーション
var PageTransition = Barba.BaseTransition.extend({
  start: function() {
    Promise
      .all([this.newContainerLoading, this.moveOut()])
      .then(this.moveIn.bind(this));
  },
  moveOut: function() {
    // 遷移前の処理(内容はお好みで)
    moveOutAnim();
    return $(this.oldContainer).animate({ opacity: 0 }, 800).promise();
  },
  moveIn: function() {
    var _this = this;
    var $el = $(this.newContainer);

    // 遷移後の処理(内容はお好みで)
    window.scrollTo( 0, 0 );
    $(this.oldContainer).hide();
    $el.css({
      visibility : 'visible',
      opacity : 0
    });
    moveInAnim();

    $el.animate({ opacity: 1 }, 400, function() {
      _this.done();
    });
  }

});

Barba.Pjax.getTransition = function() {
  return PageTransition;
};

Barba.BaseView.init()
Barba.Pjax.start();

複数ページにまたがった動きになり、少しややこしさはありますが実装するとクオリティは格段に上がるはず!!
みなさんも是非利用してみてください。

参考

Barba.jsによる画面遷移を使う
Pjax(非同期画面遷移)でシームレスな画面遷移ができるBarba.js