Swiperの紹介と、ループのバグらしき挙動に対処する

この記事はOthloTech Advent Calendar 2016 - Qiitaの9日目です。

こんにちは、MIPSのクロスコンパイルができないで影白です。

現在、ChromeのタブをMacのアプリ気切り替えやWindowsのAlt+Tab切り替えと同じようなデザインで切り替える拡張機能を作成しています。 YahooさんのReactJSハンズオンに参加できなかったのでReactJSを自力でやるか!と思い立ったやつです。 現在はまだChromeストアなどには上がってません。動くものがGitHubにありますので、興味があれば見てみてください。(現在は起動条件があまりにマニアックなので、おそらく誰もわかりません。使いたい人がいたらご連絡を・・・)

AruTab

デザインなどについては絶賛調整中ですが、フラットな感じに仕上がる予定。

ReactJSは完全にJavaScriptからソースを生成することになるので、データを元にHTMLをガリガリ書き出したい場合なんかは結構便利。ChromeExtensionでも十分活用可能です。ただし、ChromeExtensionの場合は事前コンパイルしたほうが良さげ。
ただし、万能とはいかずHTMLベースの場合は逆に面倒になりますし、ChromeExtensionのContentScript(Webページ上で実行するスクリプト)はHTMLは元ページのままなので、jQueryの力技でねじ伏せるのが一番楽だなと思いました。

さて、本当は拡張をストアに上げてChromeExtensionについて語ろうかと思ったのですが、色々と間に合わなかったのでそれは諦めました。 というか、ソースがあまりにも汚いので作り直しになります。

今回は、作成中に使ったSwiperというライブラリーの紹介と、バグ対策の記事です。

Swiperとは?

Swiper - Most Modern Mobile Touch Slider

Swiperはカルーセルやスライドなどを実現するMITライセンスのJavascriptのライブラリです。
jQueryにも対応していますが、依存はしていないのでjQuery非導入の環境でも使えるのが嬉しい。 使い方は公式のサンプル通りに書けば全く困りません。

公式のデモを見るとわかりますが、普通に使う分にはまず不便しない程度の機能が存在し、モバイルやタッチにも対応。 わりとすきがない感じです。
Swiper Demos
また、かなり大量のAPIが公開されており、自由度が高いのも素晴らしい点です。
Swiper API
いくつか比較しましたが、ここまでAPIを開放しているライブラリも珍しい気がします。
デザインもCSSで色々調整可能です。上に張った作成中の拡張機能でもページャーと左右ボタンの位置を右上に動かし、独自の画像を割り当てています。

ループのバグ

しかし、一つ不自然な挙動があります。このライブラリにはループ機能が存在します。一番端のスライドまで来ると、逆端までジャンプできるやつです。まぁ、この手のライブラリなら当然実装しておくべき機能です。
ここにバグらしき挙動が。以下の動画を御覧ください。(WebM形式)

要するに、端から端へ移動した後、点点で現在のスライドをクリックすると何故かアニメーションが発生します。 Swiper demoで再現すると思います。
これだけならまぁ、直すほどのバグでも無いのですが、実際には致命的なバグが発生します。

原因は以下のソースです。

Swiper/core.js at 35f104c9f729fea47c4958414cc4f3d4f4f5b4f0 · nolimits4web/Swiper

// s.createLoop内(2318行目周辺)
for (i = 0; i < appendSlides.length; i++) {
    s.wrapper.append($(appendSlides[i].cloneNode(true)).addClass(s.params.slideDuplicateClass));
}
for (i = prependSlides.length - 1; i >= 0; i--) {
    s.wrapper.prepend($(prependSlides[i].cloneNode(true)).addClass(s.params.slideDuplicateClass));
}

ここで、cloneNodeを行っています。ざっとしか読んでいないのですが、おそらくループ時に次のスライドがなくなるとアニメーションなどで不便が生じるので、cloneNodeしたものを追加してごまかす。という処理です。
しかし、cloneNodeでは中身は完全にコピーされるのですが、イベントがコピーされません。また、ReactJSがコピー先を書き換えることもなくなります。
今回の拡張機能ではあとでサムネイルを書き出すという処理をしているのでそれがダミースライドに適用されず、真っ白なページが表示されます。 また、クリックイベントが発生しないため色々うまく行かなくなります。

対応策

めんどくさそうですが、ソースコードを読むと意外と簡単に解決します。

const slider = $(".swiper-container");
slider.swiper({
    loop: true,
    onSlideChangeEnd : (s) => {s.fixLoop();}
});

このように、onSlideChangeEndでfixLoppを呼び出してあげるだけで解消します。ただし、fixLoopは非公開関数なので、今後のバージョンで不具合が出るかもしれません。 また、これが本当にバグなのか、パフォーマンス等の重大な問題があるのか、十分に確認できていません。
使用はご自身の責任でお願いします。

なお、Swiperのソースはコメントこそ少ないですが、かなりきれいに保たれている印象を受けました。ソースリーディングにも良いかもです。

どうにか拡張機能公開にこぎつけたいなぁ